En este estudio se va a utilizar la metodología del Descubrimiento de Conocimiento en Bases de Datos (KDD) con el objetivo de descubrir patrones, relaciones y tendencias ocultas en los datos, con el fin de tomar decisiones informadas y predecir la glucosa en los 30 y 60 minutos siguientes de los pacientes, y finalmente realizar la comparativa de soluciones con los algoritmos utilizados, las redes neuronales recurrentes (RNN) y redes neuronales recurrentes convolucionales (CRNN).
La metodlogía KDD se compone de cinco fases principales de manera iterativa (se juntan Preprocesamiento y Transformación):
Se ha seleccionado para este estudio de 110 pacientes y 4 columnas: "Patient_ID", "Measurement_date", "Measurement_time", "Measurement"
# Librerías
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import sklearn as sklearn
from sklearn import preprocessing
from sklearn.model_selection import train_test_split
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import r2_score
from sklearn.tree import DecisionTreeRegressor,DecisionTreeClassifier
from sklearn.ensemble import RandomForestRegressor,RandomForestClassifier
from sklearn.model_selection import learning_curve
from sklearn.model_selection import ShuffleSplit
from sklearn.model_selection import cross_val_score
from sklearn.metrics import classification_report
from sklearn.metrics import confusion_matrix
from sklearn.model_selection import KFold
from IPython.display import display, HTML
df = pd.read_csv("Glucose_measurements_sample.csv")
df
| Patient_ID | Measurement_date | Measurement_time | Measurement | |
|---|---|---|---|---|
| 0 | LIB193263 | 2020-06-09 | 19:08:00 | 99 |
| 1 | LIB193263 | 2020-06-09 | 19:23:00 | 92 |
| 2 | LIB193263 | 2020-06-09 | 19:38:00 | 86 |
| 3 | LIB193263 | 2020-06-09 | 19:53:00 | 85 |
| 4 | LIB193263 | 2020-06-09 | 20:08:00 | 85 |
| ... | ... | ... | ... | ... |
| 2999995 | LIB193424 | 2022-01-02 | 01:05:00 | 207 |
| 2999996 | LIB193424 | 2022-01-02 | 01:20:00 | 215 |
| 2999997 | LIB193424 | 2022-01-02 | 01:35:00 | 218 |
| 2999998 | LIB193424 | 2022-01-02 | 01:50:00 | 222 |
| 2999999 | LIB193424 | 2022-01-02 | 02:05:00 | 220 |
3000000 rows × 4 columns
El preprocesamiento y la transformación de datos son dos fases críticas. Estas fases se dividen en varias tareas:
El preprocesamiento de los datos:
La Transformación de los datos:
Estadística como la media, el mínimo y el máximo, la desviación estándar y los percentiles.
count: es la cantidad de observaciones (mediciones) en los datos. En este caso, hay 3 millones de observaciones.
mean: es la media aritmética de todas las observaciones. En este caso, el valor medio de las mediciones es de 158.95.
std: es la desviación estándar, que mide la cantidad de variación o dispersión que hay en los datos. Una desviación estándar mayor indica que los datos están más dispersos y tienen más variabilidad. En este caso, la desviación estándar es de 65.43.
min: es el valor mínimo en los datos. En este caso, el valor más bajo en las mediciones es 40. Se debe a que el freestlye libre 2 no responde a niveles de glucosa por debajo de 40 mg/dl.
25%: es el primer cuartil, es decir, el valor que es mayor que el 25% de las observaciones. En este caso, el 25% de las mediciones tienen un valor de 110 o menos.
50%: es el segundo cuartil, es decir, el valor que es mayor que el 50% de las observaciones (también conocido como la mediana). En este caso, la mediana de las mediciones es 148.
75%: es el tercer cuartil, es decir, el valor que es mayor que el 75% de las observaciones. En este caso, el 75% de las mediciones tienen un valor de 197 o menos.
max: es el valor máximo en los datos. En este caso, el valor más alto en las mediciones es 500. Se debe a que el freestlye libre 2 no responde a niveles de glucosa por encima de 500 mg/dl.
Los valores de los percentiles (25%, 50% y 75%) proporcionan cómo se distribuyen los datos alrededor de la mediana. En este caso, el hecho de que el tercer cuartil sea 197 significa que el 25% de las mediciones tienen un valor entre 148 y 197.
# Mostrar estadística, como extremos atípicos y grandes desviaciones
df.describe().round(2)
| Measurement | |
|---|---|
| count | 3000000.00 |
| mean | 158.95 |
| std | 65.43 |
| min | 40.00 |
| 25% | 110.00 |
| 50% | 148.00 |
| 75% | 197.00 |
| max | 500.00 |
Los valores de los datos (string/object, float, int, boolean) son importantes para el manejo de los datos.
Para un mejor manejo de los datos con Pandas, es recomendable que la columna "Patient_ID" contenga valores enteros en lugar de cadenas de texto. Además, para poder aprovechar al máximo las funcionalidades de Pandas, es conveniente que las columnas "Measurement_date" y "Measurement_time" se conviertan a formato de fecha y hora (datetime).
# Los tipos de valores de los datos son float o enteros
df.dtypes
Patient_ID object Measurement_date object Measurement_time object Measurement int64 dtype: object
**Limpieza de datos y Transformación de los datos**
La limpieza de los datos es una parte crucial del proceso de obtención, ya que permite adecuar el contenido a nuestras necesidades y hacerlos más fáciles de utilizar. Una forma de mejorar la eficiencia del uso de los datos es renombrar los valores de la columna "Patient_ID" y de las columnas "Measurement_date" y "Measurement_time" para simplificar su uso en pandas y reducir su espacio en pantalla.
# Verificar si hay valores nulos
if df.isnull().any().any():
print("Hay valores nulos en el dataframe")
else:
print("No hay valores nulos en el dataframe")
# Verificar si hay valores faltantes
if df.isna().any().any():
print("Hay valores faltantes en el dataframe")
else:
print("No hay valores faltantes en el dataframe")
No hay valores nulos en el dataframe No hay valores faltantes en el dataframe
# Obtener una lista de valores únicos de la columna Patient_ID del DataFrame df
unique_patient_ids = df['Patient_ID'].unique()
# Iterar sobre la lista de unique_patient_ids para ver el número de cada paciente único
for i, patient_id in enumerate(unique_patient_ids):
# Imprimir el número del paciente actual (i) y su ID (patient_id)
print(f'Número para el paciente {patient_id}: {i}')
Número para el paciente LIB193263: 0 Número para el paciente LIB193264: 1 Número para el paciente LIB193265: 2 Número para el paciente LIB193266: 3 Número para el paciente LIB193267: 4 Número para el paciente LIB193268: 5 Número para el paciente LIB193269: 6 Número para el paciente LIB193272: 7 Número para el paciente LIB193273: 8 Número para el paciente LIB193274: 9 Número para el paciente LIB193276: 10 Número para el paciente LIB193277: 11 Número para el paciente LIB193278: 12 Número para el paciente LIB193279: 13 Número para el paciente LIB193280: 14 Número para el paciente LIB193281: 15 Número para el paciente LIB193282: 16 Número para el paciente LIB193283: 17 Número para el paciente LIB193284: 18 Número para el paciente LIB193302: 19 Número para el paciente LIB193303: 20 Número para el paciente LIB193304: 21 Número para el paciente LIB193307: 22 Número para el paciente LIB193308: 23 Número para el paciente LIB193309: 24 Número para el paciente LIB193310: 25 Número para el paciente LIB193311: 26 Número para el paciente LIB193312: 27 Número para el paciente LIB193313: 28 Número para el paciente LIB193314: 29 Número para el paciente LIB193315: 30 Número para el paciente LIB193317: 31 Número para el paciente LIB193318: 32 Número para el paciente LIB193319: 33 Número para el paciente LIB193320: 34 Número para el paciente LIB193324: 35 Número para el paciente LIB193325: 36 Número para el paciente LIB193326: 37 Número para el paciente LIB193328: 38 Número para el paciente LIB193330: 39 Número para el paciente LIB193332: 40 Número para el paciente LIB193333: 41 Número para el paciente LIB193334: 42 Número para el paciente LIB193335: 43 Número para el paciente LIB193337: 44 Número para el paciente LIB193338: 45 Número para el paciente LIB193340: 46 Número para el paciente LIB193341: 47 Número para el paciente LIB193342: 48 Número para el paciente LIB193343: 49 Número para el paciente LIB193344: 50 Número para el paciente LIB193345: 51 Número para el paciente LIB193346: 52 Número para el paciente LIB193347: 53 Número para el paciente LIB193349: 54 Número para el paciente LIB193350: 55 Número para el paciente LIB193351: 56 Número para el paciente LIB193352: 57 Número para el paciente LIB193353: 58 Número para el paciente LIB193354: 59 Número para el paciente LIB193356: 60 Número para el paciente LIB193357: 61 Número para el paciente LIB193358: 62 Número para el paciente LIB193361: 63 Número para el paciente LIB193363: 64 Número para el paciente LIB193365: 65 Número para el paciente LIB193366: 66 Número para el paciente LIB193367: 67 Número para el paciente LIB193368: 68 Número para el paciente LIB193369: 69 Número para el paciente LIB193370: 70 Número para el paciente LIB193371: 71 Número para el paciente LIB193372: 72 Número para el paciente LIB193375: 73 Número para el paciente LIB193376: 74 Número para el paciente LIB193377: 75 Número para el paciente LIB193378: 76 Número para el paciente LIB193379: 77 Número para el paciente LIB193380: 78 Número para el paciente LIB193381: 79 Número para el paciente LIB193382: 80 Número para el paciente LIB193383: 81 Número para el paciente LIB193384: 82 Número para el paciente LIB193385: 83 Número para el paciente LIB193386: 84 Número para el paciente LIB193387: 85 Número para el paciente LIB193389: 86 Número para el paciente LIB193390: 87 Número para el paciente LIB193391: 88 Número para el paciente LIB193392: 89 Número para el paciente LIB193393: 90 Número para el paciente LIB193395: 91 Número para el paciente LIB193397: 92 Número para el paciente LIB193398: 93 Número para el paciente LIB193399: 94 Número para el paciente LIB193400: 95 Número para el paciente LIB193404: 96 Número para el paciente LIB193406: 97 Número para el paciente LIB193407: 98 Número para el paciente LIB193408: 99 Número para el paciente LIB193410: 100 Número para el paciente LIB193411: 101 Número para el paciente LIB193412: 102 Número para el paciente LIB193414: 103 Número para el paciente LIB193416: 104 Número para el paciente LIB193418: 105 Número para el paciente LIB193419: 106 Número para el paciente LIB193420: 107 Número para el paciente LIB193423: 108 Número para el paciente LIB193424: 109
from sklearn.preprocessing import LabelEncoder
# crear una instancia de LabelEncoder()
le = LabelEncoder()
# convertir las columnas a formato numérico utilizando LabelEncoder()
df['Patient_ID'] = le.fit_transform(df['Patient_ID'])
# Los tipos de valores de los datos son float o enteros
df.dtypes
Patient_ID int32 Measurement_date object Measurement_time object Measurement int64 dtype: object
df['Measurement_date'] = pd.to_datetime(df['Measurement_date'])
df['Measurement_time'] = pd.to_datetime(df['Measurement_time'], format='%H:%M:%S')
df.dtypes
Patient_ID int32 Measurement_date datetime64[ns] Measurement_time datetime64[ns] Measurement int64 dtype: object
**Extracción de característcas**
La extracción de características consiste en conseguir optimizar el entrenamiento del modelo de predicción de niveles de glucosa en sangre. Se agrega varias columnas al conjunto de datos, como "In_Range", "Hyperglycemia", "Hypoglycemia", "Hour_of_Day" y "Day_of_Week", para analizar cómo estas características afectan la precisión del modelo.
Esta información puede mejorar la precisión del modelo ya que las mediciones que están fuera del rango normal y las mediciones más recientes son más relevantes para predecir los niveles futuros de glucosa, mientras que las características de hora del día y día de la semana pueden ayudar al modelo a capturar patrones diarios y semanales en los niveles de glucosa, lo cual puede mejorar la precisión de las predicciones.
# Crear una nueva columna llamada 'In_Range' que sea True si la medición está dentro del rango y False si no lo está
df['In_Range'] = (df['Measurement'] >= 70) & (df['Measurement'] <= 180)
# Mostrar el resultado
df
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | |
|---|---|---|---|---|---|
| 0 | 0 | 2020-06-09 | 1900-01-01 19:08:00 | 99 | True |
| 1 | 0 | 2020-06-09 | 1900-01-01 19:23:00 | 92 | True |
| 2 | 0 | 2020-06-09 | 1900-01-01 19:38:00 | 86 | True |
| 3 | 0 | 2020-06-09 | 1900-01-01 19:53:00 | 85 | True |
| 4 | 0 | 2020-06-09 | 1900-01-01 20:08:00 | 85 | True |
| ... | ... | ... | ... | ... | ... |
| 2999995 | 109 | 2022-01-02 | 1900-01-01 01:05:00 | 207 | False |
| 2999996 | 109 | 2022-01-02 | 1900-01-01 01:20:00 | 215 | False |
| 2999997 | 109 | 2022-01-02 | 1900-01-01 01:35:00 | 218 | False |
| 2999998 | 109 | 2022-01-02 | 1900-01-01 01:50:00 | 222 | False |
| 2999999 | 109 | 2022-01-02 | 1900-01-01 02:05:00 | 220 | False |
3000000 rows × 5 columns
# Crear una nueva columna llamada 'Hypoglycemia' que sea True si la medición está por debajo de 70 mg/dl y False si no lo está
df['Hypoglycemia'] = (df['Measurement'] < 70)
# Crear una nueva columna llamada 'Hyperglycemia' que sea True si la medición está por encima de 180 mg/dl y False si no lo está
df['Hyperglycemia'] = (df['Measurement'] > 180)
# Mostrar el resultado
df
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|---|---|---|---|
| 0 | 0 | 2020-06-09 | 1900-01-01 19:08:00 | 99 | True | False | False |
| 1 | 0 | 2020-06-09 | 1900-01-01 19:23:00 | 92 | True | False | False |
| 2 | 0 | 2020-06-09 | 1900-01-01 19:38:00 | 86 | True | False | False |
| 3 | 0 | 2020-06-09 | 1900-01-01 19:53:00 | 85 | True | False | False |
| 4 | 0 | 2020-06-09 | 1900-01-01 20:08:00 | 85 | True | False | False |
| ... | ... | ... | ... | ... | ... | ... | ... |
| 2999995 | 109 | 2022-01-02 | 1900-01-01 01:05:00 | 207 | False | False | True |
| 2999996 | 109 | 2022-01-02 | 1900-01-01 01:20:00 | 215 | False | False | True |
| 2999997 | 109 | 2022-01-02 | 1900-01-01 01:35:00 | 218 | False | False | True |
| 2999998 | 109 | 2022-01-02 | 1900-01-01 01:50:00 | 222 | False | False | True |
| 2999999 | 109 | 2022-01-02 | 1900-01-01 02:05:00 | 220 | False | False | True |
3000000 rows × 7 columns
# Convertir la columna 'Measurement_time' a formato de hora de Pandas
df['Measurement_time'] = pd.to_datetime(df['Measurement_time'], format='%H:%M:%S').dt.time
# Extraer la hora del día de la columna 'Measurement_time'
df['Hour_of_Day'] = df['Measurement_time'].apply(lambda x: x.hour)
# Mostrar el resultado
df
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | |
|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 2020-06-09 | 19:08:00 | 99 | True | False | False | 19 |
| 1 | 0 | 2020-06-09 | 19:23:00 | 92 | True | False | False | 19 |
| 2 | 0 | 2020-06-09 | 19:38:00 | 86 | True | False | False | 19 |
| 3 | 0 | 2020-06-09 | 19:53:00 | 85 | True | False | False | 19 |
| 4 | 0 | 2020-06-09 | 20:08:00 | 85 | True | False | False | 20 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2999995 | 109 | 2022-01-02 | 01:05:00 | 207 | False | False | True | 1 |
| 2999996 | 109 | 2022-01-02 | 01:20:00 | 215 | False | False | True | 1 |
| 2999997 | 109 | 2022-01-02 | 01:35:00 | 218 | False | False | True | 1 |
| 2999998 | 109 | 2022-01-02 | 01:50:00 | 222 | False | False | True | 1 |
| 2999999 | 109 | 2022-01-02 | 02:05:00 | 220 | False | False | True | 2 |
3000000 rows × 8 columns
# Convertir la columna 'Measurement_date' a formato de fecha de Pandas
df['Measurement_date'] = pd.to_datetime(df['Measurement_date'])
# Extraer el día de la semana de la columna 'Measurement_date'
df['Day_of_Week'] = df['Measurement_date'].dt.day_name()
# Mostrar el resultado
df
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| 0 | 0 | 2020-06-09 | 19:08:00 | 99 | True | False | False | 19 | Tuesday |
| 1 | 0 | 2020-06-09 | 19:23:00 | 92 | True | False | False | 19 | Tuesday |
| 2 | 0 | 2020-06-09 | 19:38:00 | 86 | True | False | False | 19 | Tuesday |
| 3 | 0 | 2020-06-09 | 19:53:00 | 85 | True | False | False | 19 | Tuesday |
| 4 | 0 | 2020-06-09 | 20:08:00 | 85 | True | False | False | 20 | Tuesday |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2999995 | 109 | 2022-01-02 | 01:05:00 | 207 | False | False | True | 1 | Sunday |
| 2999996 | 109 | 2022-01-02 | 01:20:00 | 215 | False | False | True | 1 | Sunday |
| 2999997 | 109 | 2022-01-02 | 01:35:00 | 218 | False | False | True | 1 | Sunday |
| 2999998 | 109 | 2022-01-02 | 01:50:00 | 222 | False | False | True | 1 | Sunday |
| 2999999 | 109 | 2022-01-02 | 02:05:00 | 220 | False | False | True | 2 | Sunday |
3000000 rows × 9 columns
df.dtypes
Patient_ID int32 Measurement_date datetime64[ns] Measurement_time object Measurement int64 In_Range bool Hypoglycemia bool Hyperglycemia bool Hour_of_Day int64 Day_of_Week object dtype: object
# Convierte la columna 'Measurement_time' en un objeto datetime de Pandas. En formato '%H:%M:%S' para la hora
df['Measurement_time'] = pd.to_datetime(df['Measurement_time'], format='%H:%M:%S')
# Extraer el día de la semana como un número entero de la columna 'Measurement_date'. 0 es lunes y 6 es domingo
df['Day_of_Week'] = df['Measurement_date'].dt.dayofweek
# Muestra los tipos de datos de cada columna en el DataFrame
df.dtypes
Patient_ID int32 Measurement_date datetime64[ns] Measurement_time datetime64[ns] Measurement int64 In_Range bool Hypoglycemia bool Hyperglycemia bool Hour_of_Day int64 Day_of_Week int64 dtype: object
Las series temporales son una colección de puntos de datos recogidos a intervalos regulares de tiempo. En este contexto, son esenciales para la detección de tendencias, predicciones y patrones. Estos análisis temporales son vitales en numerosos campos, incluyendo las finanzas, la economía, y en este caso específico, en el monitoreo de la glucosa, donde se utilizan para prever niveles de glucosa.
Preparación de Series Temporales:
La preparación se enfoca para la construcción de un modelo basado en series temporales el cual se va a diseñar para predecir los niveles de glucosa en un periodo de 15 y 30 minutos. Para lograr esto, el conjunto de datos en series temporales se va a preparar con intervalos de 15 minutos teniendo observaciones equidistantes en el tiempo entre las fechas de medición.
Posterior a la preparación de Series Temporales:
Posterior a la preparación se va a entrenar el modelo con una longitud de secuencia (seq_length) de 8, que se refiere a la cantidad de pesos temporales que se utilizan como entrada para predecir la salida. Para predecir los próximos 15 y 30 minutos se utiliza un bucle for. Esto permite generar múltiples predicciones en secuencia. Adicionalmente, se utilizado la función pd.date_range() de la biblioteca de Python pandas. Esta función, con un intervalo de 15 minutos y un valor de 'períodos' establecido en 3, crea un rango de fechas que se inicia desde la última fecha conocida (contada como el primer periodo) y genera dos fechas adicionales, cada una en un intervalo de 15 minutos. De esta manera, el total de períodos cubiertos abarca los próximos 15 y 30 minutos desde la última fecha registrada.
Se crea un nuevo DataFrame que organiza los datos en intervalos de tiempo regulares en frecuencias de 15 minutos. Además, se han rellenado los valores faltantes mediante la interpolación lineal, lo que permite analizar los datos como una serie temporal con observaciones equidistantes en el tiempo.
import pandas as pd
# Ordenar el DataFrame por fecha y hora con Datetime
df['Datetime'] = pd.to_datetime(df['Measurement_date']) + pd.to_timedelta(df['Measurement_time'].dt.hour, unit='h') + pd.to_timedelta(df['Measurement_time'].dt.minute, unit='m')
df = df.sort_values(by='Datetime')
# Crear un nuevo DataFrame con los datos en serie temporal
df = df.resample('15T', on='Datetime').agg({
'Patient_ID': 'first',
'Measurement_date': 'first',
'Measurement_time': 'first',
'Measurement': 'mean',
'In_Range': 'first',
'Hypoglycemia': 'first',
'Hyperglycemia': 'first',
'Hour_of_Day': 'first',
'Day_of_Week': 'first'
})
# Rellenar los valores faltantes
df['Measurement'].interpolate(method='linear', inplace=True)
# Redondear los valores en la columna 'Measurement'
df['Measurement'] = df['Measurement'].round()
# Convertir el tipo de datos de cada columna al tipo de datos original
df = df.astype({
'Patient_ID': 'Int32',
'Measurement': 'Int64',
'In_Range': 'bool',
'Hypoglycemia': 'bool',
'Hyperglycemia': 'bool',
'Hour_of_Day': 'Int64',
'Day_of_Week': 'Int64'
})
# Verificar si hay valores nulos
if df.isnull().any().any():
print("Hay valores nulos en el dataframe")
else:
print("No hay valores nulos en el dataframe")
# Verificar si hay valores faltantes
if df.isna().any().any():
print("Hay valores faltantes en el dataframe")
else:
print("No hay valores faltantes en el dataframe")
Hay valores nulos en el dataframe Hay valores faltantes en el dataframe
# Obtener una lista de pacientes únicos
pacientes = df["Patient_ID"].unique()
print(pacientes)
print("\n")
# Crear un DataFrame transpuesto con la lista de pacientes
df_pacientes = pd.DataFrame(pacientes).T
# Mostrar el DataFrame transpuesto
print(df_pacientes.to_string(index=False, header=False))
# Encontrar los ID de pacientes faltantes
missing_patients = [patient for patient in range(0, 110) if patient not in pacientes]
print(f"Paciente faltante: {missing_patients}")
<IntegerArray> [ 83, <NA>, 48, 92, 11, 24, 5, 91, 22, 2, ... 10, 44, 13, 103, 64, 57, 38, 33, 65, 7] Length: 110, dtype: Int32 83 <NA> 48 92 11 24 5 91 22 2 6 40 47 12 43 69 55 60 71 100 75 67 93 59 25 17 74 87 54 21 4 1 0 29 96 84 14 104 86 94 88 3 78 62 99 53 34 68 80 82 50 16 30 39 46 41 77 19 42 28 58 102 95 61 18 81 35 97 98 107 106 26 70 9 49 45 23 73 32 63 27 76 79 15 8 36 56 52 72 31 85 101 108 89 109 90 105 51 20 37 10 44 13 103 64 57 38 33 65 7 Paciente faltante: [66]
# Verificar valores faltantes o nulos en todo el DataFrame
print(df.isnull().sum())
# Verificar valores faltantes o nulos en una columna específica
print(df['Patient_ID'].isnull().sum())
Patient_ID 6609 Measurement_date 6609 Measurement_time 6609 Measurement 0 In_Range 0 Hypoglycemia 0 Hyperglycemia 0 Hour_of_Day 6609 Day_of_Week 6609 dtype: int64 6609
# Crear un nuevo DataFrame que excluye filas donde la columna Patient_ID tiene un valor de 66
df_clean = df[df['Patient_ID'] != 66]
# Verificar valores faltantes o nulos en todo el DataFrame
print(df_clean.isnull().sum())
# Verificar valores faltantes o nulos en una columna específica
print(df_clean['Patient_ID'].isnull().sum())
Patient_ID 0 Measurement_date 0 Measurement_time 0 Measurement 0 In_Range 0 Hypoglycemia 0 Hyperglycemia 0 Hour_of_Day 0 Day_of_Week 0 dtype: int64 0
# Crear un nuevo DataFrame que excluye filas donde la columna Patient_ID tiene un valor de 66
df = df[df['Patient_ID'] != 66]
# Verificar valores faltantes o nulos en todo el DataFrame
print(df.isnull().sum())
# Verificar valores faltantes o nulos en una columna específica
print(df['Patient_ID'].isnull().sum())
Patient_ID 0 Measurement_date 0 Measurement_time 0 Measurement 0 In_Range 0 Hypoglycemia 0 Hyperglycemia 0 Hour_of_Day 0 Day_of_Week 0 dtype: int64 0
# Verificar si hay valores nulos
if df.isnull().any().any():
print("Hay valores nulos en el dataframe")
else:
print("No hay valores nulos en el dataframe")
# Verificar si hay valores faltantes
if df.isna().any().any():
print("Hay valores faltantes en el dataframe")
else:
print("No hay valores faltantes en el dataframe")
No hay valores nulos en el dataframe No hay valores faltantes en el dataframe
En esta sección se va a llevar a cabo toda la identificación de patrones y relaciones, análisis exploratorio y estadístico y el uso de algoritmos de aprendizaje automático profundo RNN y CRNN.
Ahora los pacientes son del 0 hasta el 109 y las columnas Measurement_date y Measurement_time son datatime permitiendo manejar sus fechas con pandas
df
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-02-21 19:45:00 | 83 | 2018-02-21 | 1900-01-01 19:46:00 | 121 | True | False | False | 19 | 2 |
| 2018-02-21 20:00:00 | 83 | 2018-02-21 | 1900-01-01 20:01:00 | 124 | True | False | False | 20 | 2 |
| 2018-02-21 20:15:00 | 83 | 2018-02-21 | 1900-01-01 20:17:00 | 131 | True | False | False | 20 | 2 |
| 2018-02-21 20:30:00 | 83 | 2018-02-21 | 1900-01-01 20:32:00 | 128 | True | False | False | 20 | 2 |
| 2018-02-21 20:45:00 | 83 | 2018-02-21 | 1900-01-01 20:47:00 | 129 | True | False | False | 20 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-21 19:45:00 | 27 | 2022-03-21 | 1900-01-01 19:48:00 | 264 | False | False | True | 19 | 0 |
| 2022-03-21 20:00:00 | 27 | 2022-03-21 | 1900-01-01 20:03:00 | 269 | False | False | True | 20 | 0 |
| 2022-03-21 20:15:00 | 27 | 2022-03-21 | 1900-01-01 20:18:00 | 283 | False | False | True | 20 | 0 |
| 2022-03-21 20:30:00 | 27 | 2022-03-21 | 1900-01-01 20:33:00 | 315 | False | False | True | 20 | 0 |
| 2022-03-21 20:45:00 | 27 | 2022-03-21 | 1900-01-01 20:48:00 | 327 | False | False | True | 20 | 0 |
136340 rows × 9 columns
df.dtypes
Patient_ID Int32 Measurement_date datetime64[ns] Measurement_time datetime64[ns] Measurement Int64 In_Range bool Hypoglycemia bool Hyperglycemia bool Hour_of_Day Int64 Day_of_Week Int64 dtype: object
pacientes = df["Patient_ID"].unique()
print(pacientes)
print("\n")
df_pacientes = pd.DataFrame(pacientes).T
# Display the transposed DataFrame
print(df_pacientes.to_string(index=False, header=False))
# Find missing patient IDs
missing_patients = [patient for patient in range(0, 110) if patient not in pacientes]
print(f"Paciente faltante: {missing_patients}")
<IntegerArray> [ 83, 48, 92, 11, 24, 5, 91, 22, 2, 6, ... 10, 44, 13, 103, 64, 57, 38, 33, 65, 7] Length: 109, dtype: Int32 83 48 92 11 24 5 91 22 2 6 40 47 12 43 69 55 60 71 100 75 67 93 59 25 17 74 87 54 21 4 1 0 29 96 84 14 104 86 94 88 3 78 62 99 53 34 68 80 82 50 16 30 39 46 41 77 19 42 28 58 102 95 61 18 81 35 97 98 107 106 26 70 9 49 45 23 73 32 63 27 76 79 15 8 36 56 52 72 31 85 101 108 89 109 90 105 51 20 37 10 44 13 103 64 57 38 33 65 7 Paciente faltante: [66]
# Obtener el conteo de valores para la columna 'Patient_ID'
df_vc = df['Patient_ID'].value_counts().reset_index()
# Renombrar las columnas para que sean más descriptivas
df_vc.columns = ['Patient_ID', 'Count']
# Mostrar el resultado
df_vc
| Patient_ID | Count | |
|---|---|---|
| 0 | 24 | 11987 |
| 1 | 11 | 11332 |
| 2 | 22 | 10409 |
| 3 | 83 | 9198 |
| 4 | 92 | 8509 |
| ... | ... | ... |
| 104 | 33 | 38 |
| 105 | 79 | 27 |
| 106 | 57 | 26 |
| 107 | 65 | 20 |
| 108 | 103 | 11 |
109 rows × 2 columns
df.groupby('Patient_ID')['Measurement_date'].agg(['min', 'max'])
| min | max | |
|---|---|---|
| Patient_ID | ||
| 0 | 2020-06-12 | 2022-03-18 |
| 1 | 2020-06-10 | 2022-03-06 |
| 2 | 2019-01-27 | 2022-03-19 |
| 3 | 2020-10-10 | 2022-03-11 |
| 4 | 2020-06-10 | 2022-03-17 |
| ... | ... | ... |
| 105 | 2021-10-29 | 2022-03-15 |
| 106 | 2021-06-18 | 2022-03-14 |
| 107 | 2021-06-17 | 2022-03-11 |
| 108 | 2021-10-25 | 2022-03-18 |
| 109 | 2021-10-28 | 2022-01-01 |
109 rows × 2 columns
df.loc[df["Patient_ID"] == 0]
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2020-06-12 00:15:00 | 0 | 2020-06-12 | 1900-01-01 00:16:00 | 121 | True | False | False | 0 | 4 |
| 2020-06-12 00:30:00 | 0 | 2020-06-12 | 1900-01-01 00:31:00 | 123 | True | False | False | 0 | 4 |
| 2020-06-12 00:45:00 | 0 | 2020-06-12 | 1900-01-01 00:46:00 | 132 | True | False | False | 0 | 4 |
| 2020-06-12 10:00:00 | 0 | 2020-06-12 | 1900-01-01 10:01:00 | 155 | True | False | False | 10 | 4 |
| 2020-06-12 10:15:00 | 0 | 2020-06-12 | 1900-01-01 10:16:00 | 158 | True | False | False | 10 | 4 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-18 02:00:00 | 0 | 2022-03-18 | 1900-01-01 02:00:00 | 170 | False | False | True | 2 | 4 |
| 2022-03-18 02:15:00 | 0 | 2022-03-18 | 1900-01-01 02:15:00 | 170 | False | False | True | 2 | 4 |
| 2022-03-18 06:15:00 | 0 | 2022-03-18 | 1900-01-01 06:15:00 | 154 | False | False | True | 6 | 4 |
| 2022-03-18 07:45:00 | 0 | 2022-03-18 | 1900-01-01 07:45:00 | 153 | False | False | True | 7 | 4 |
| 2022-03-18 11:15:00 | 0 | 2022-03-18 | 1900-01-01 11:15:00 | 173 | False | False | True | 11 | 4 |
1675 rows × 9 columns
**Dataframe más pequeño para poder manejarlo (5 pacientes)**
Se ha realizado una reducción de datos del DataFrame debido a que la cantidad de información era demasiado grande para ser procesada por la capacidad de cómputo disponible en la computadora utilizada.
Podemos ver que hay 51435 filas.
# Crear un nuevo DataFrame con los 5 pacientes con más datos
top_5_pacientes = df.groupby('Patient_ID')['Measurement_time'].count().sort_values(ascending=False).head(5).index
df_top_5_pacientes = df[df['Patient_ID'].isin(top_5_pacientes)]
df_top_5_pacientes
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-02-21 19:45:00 | 83 | 2018-02-21 | 1900-01-01 19:46:00 | 121 | True | False | False | 19 | 2 |
| 2018-02-21 20:00:00 | 83 | 2018-02-21 | 1900-01-01 20:01:00 | 124 | True | False | False | 20 | 2 |
| 2018-02-21 20:15:00 | 83 | 2018-02-21 | 1900-01-01 20:17:00 | 131 | True | False | False | 20 | 2 |
| 2018-02-21 20:30:00 | 83 | 2018-02-21 | 1900-01-01 20:32:00 | 128 | True | False | False | 20 | 2 |
| 2018-02-21 20:45:00 | 83 | 2018-02-21 | 1900-01-01 20:47:00 | 129 | True | False | False | 20 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-14 07:00:00 | 83 | 2022-03-14 | 1900-01-01 07:00:00 | 160 | True | False | False | 7 | 0 |
| 2022-03-14 07:15:00 | 83 | 2022-03-14 | 1900-01-01 07:15:00 | 164 | True | False | False | 7 | 0 |
| 2022-03-14 10:00:00 | 83 | 2022-03-14 | 1900-01-01 10:00:00 | 182 | True | False | False | 10 | 0 |
| 2022-03-14 10:45:00 | 83 | 2022-03-14 | 1900-01-01 10:45:00 | 168 | True | False | False | 10 | 0 |
| 2022-03-15 23:45:00 | 22 | 2022-03-15 | 1900-01-01 23:46:00 | 155 | True | False | False | 23 | 1 |
51435 rows × 9 columns
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y contar el número de mediciones ('Measurement_time') que cada paciente ha realizado.
# Luego ordenar los resultados de manera descendente ('sort_values(ascending=False)') y seleccionar los primeros 5 pacientes con más mediciones ('head(5)').
top_5_pacientes = df.groupby('Patient_ID')['Measurement_time'].count().sort_values(ascending=False).head(5)
# Crear un nuevo DataFrame 'df_top_5_pacientes_modificado' que contiene los mismos datos que 'top_5_pacientes',
# pero reseteamos el índice para que el ID de paciente y el número de mediciones estén en columnas separadas.
df_top_5_pacientes_modificado = top_5_pacientes.reset_index()
# Renombrar las columnas del DataFrame 'df_top_5_pacientes_modificado'.
df_top_5_pacientes_modificado.columns = ['Patient_ID', 'Num_Mediciones']
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y obtener la fecha mínima ('min') y máxima ('max') de las mediciones.
# Luego resetear el índice y se guarda en el DataFrame 'fechas_min_max'.
fechas_min_max = df.groupby('Patient_ID')['Measurement_date'].agg(['min', 'max']).reset_index()
# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'fechas_min_max' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(fechas_min_max, on='Patient_ID')
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y calcular la media de la columna 'Measurement' para cada paciente
media_glucosa = df.groupby('Patient_ID')['Measurement'].mean()
# Crear un nuevo DataFrame 'df_media_glucosa' que contiene los mismos datos que 'media_glucosa',
# pero reseteamos el índice para que el ID de paciente y la media de glucosa estén en columnas separadas.
df_media_glucosa = media_glucosa.reset_index()
# Renombrar las columnas del DataFrame 'df_media_glucosa'.
df_media_glucosa.columns = ['Patient_ID', 'Media_Glucosa']
# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'df_media_glucosa' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(df_media_glucosa, on='Patient_ID')
df_top_5_pacientes_modificado
| Patient_ID | Num_Mediciones | min | max | Media_Glucosa | |
|---|---|---|---|---|---|
| 0 | 24 | 11987 | 2018-07-13 | 2022-03-12 | 151.588888 |
| 1 | 11 | 11332 | 2018-06-12 | 2022-02-25 | 150.851747 |
| 2 | 22 | 10409 | 2018-11-06 | 2022-03-15 | 157.267653 |
| 3 | 83 | 9198 | 2018-02-21 | 2022-03-14 | 144.642314 |
| 4 | 92 | 8509 | 2018-06-06 | 2022-01-10 | 152.568692 |
df_verano = df_top_5_pacientes[df_top_5_pacientes['Measurement_date'].dt.month.isin([6,7,8])]
df_verano
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-06-06 11:30:00 | 92 | 2018-06-06 | 1900-01-01 11:38:00 | 372 | False | False | True | 11 | 2 |
| 2018-06-06 11:45:00 | 92 | 2018-06-06 | 1900-01-01 11:53:00 | 365 | False | False | True | 11 | 2 |
| 2018-06-06 12:00:00 | 92 | 2018-06-06 | 1900-01-01 12:08:00 | 345 | False | False | True | 12 | 2 |
| 2018-06-06 12:15:00 | 92 | 2018-06-06 | 1900-01-01 12:23:00 | 327 | False | False | True | 12 | 2 |
| 2018-06-06 12:30:00 | 92 | 2018-06-06 | 1900-01-01 12:38:00 | 305 | False | False | True | 12 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2021-08-30 23:30:00 | 83 | 2021-08-30 | 1900-01-01 23:30:00 | 150 | True | False | False | 23 | 0 |
| 2021-08-31 16:45:00 | 83 | 2021-08-31 | 1900-01-01 16:45:00 | 155 | False | False | True | 16 | 1 |
| 2021-08-31 17:45:00 | 83 | 2021-08-31 | 1900-01-01 17:45:00 | 157 | True | False | False | 17 | 1 |
| 2021-08-31 18:00:00 | 83 | 2021-08-31 | 1900-01-01 18:00:00 | 158 | True | False | False | 18 | 1 |
| 2021-08-31 18:45:00 | 83 | 2021-08-31 | 1900-01-01 18:45:00 | 161 | True | False | False | 18 | 1 |
13634 rows × 9 columns
df_inverno = df_top_5_pacientes[df_top_5_pacientes['Measurement_date'].dt.month.isin([12,1,2,3])]
df_inverno
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-02-21 19:45:00 | 83 | 2018-02-21 | 1900-01-01 19:46:00 | 121 | True | False | False | 19 | 2 |
| 2018-02-21 20:00:00 | 83 | 2018-02-21 | 1900-01-01 20:01:00 | 124 | True | False | False | 20 | 2 |
| 2018-02-21 20:15:00 | 83 | 2018-02-21 | 1900-01-01 20:17:00 | 131 | True | False | False | 20 | 2 |
| 2018-02-21 20:30:00 | 83 | 2018-02-21 | 1900-01-01 20:32:00 | 128 | True | False | False | 20 | 2 |
| 2018-02-21 20:45:00 | 83 | 2018-02-21 | 1900-01-01 20:47:00 | 129 | True | False | False | 20 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-14 07:00:00 | 83 | 2022-03-14 | 1900-01-01 07:00:00 | 160 | True | False | False | 7 | 0 |
| 2022-03-14 07:15:00 | 83 | 2022-03-14 | 1900-01-01 07:15:00 | 164 | True | False | False | 7 | 0 |
| 2022-03-14 10:00:00 | 83 | 2022-03-14 | 1900-01-01 10:00:00 | 182 | True | False | False | 10 | 0 |
| 2022-03-14 10:45:00 | 83 | 2022-03-14 | 1900-01-01 10:45:00 | 168 | True | False | False | 10 | 0 |
| 2022-03-15 23:45:00 | 22 | 2022-03-15 | 1900-01-01 23:46:00 | 155 | True | False | False | 23 | 1 |
17535 rows × 9 columns
# Calcular las medias para cada DataFrame
media_df = df['Measurement'].mean()
media_df_top_5_pacientes = df_top_5_pacientes['Measurement'].mean()
media_df_verano = df_verano['Measurement'].mean()
media_df_inverno = df_inverno['Measurement'].mean()
# Crear un nuevo DataFrame con los valores de las medias
df_medias = pd.DataFrame({'DataFrame': ['df (completo)', 'df_top_5_pacientes', 'df_verano (top_5_pacientes)', 'df_inverno (top_5_pacientes)'],
'Media': [media_df, media_df_top_5_pacientes, media_df_verano, media_df_inverno]})
# Mostrar el resultado
df_medias
| DataFrame | Media | |
|---|---|---|
| 0 | df (completo) | 154.892599 |
| 1 | df_top_5_pacientes | 151.495557 |
| 2 | df_verano (top_5_pacientes) | 144.856829 |
| 3 | df_inverno (top_5_pacientes) | 154.311263 |
# Seleccionar las filas con Measurement (medición de glucosa) entre 70 mg/dl y 180 mg/dl (Tiempo en rango ideal según TIR)
df_top_5_pacientes[(df_top_5_pacientes['Measurement'] >= 70) & (df_top_5_pacientes['Measurement'] <= 180)]
df_top_5_pacientes
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-02-21 19:45:00 | 83 | 2018-02-21 | 1900-01-01 19:46:00 | 121 | True | False | False | 19 | 2 |
| 2018-02-21 20:00:00 | 83 | 2018-02-21 | 1900-01-01 20:01:00 | 124 | True | False | False | 20 | 2 |
| 2018-02-21 20:15:00 | 83 | 2018-02-21 | 1900-01-01 20:17:00 | 131 | True | False | False | 20 | 2 |
| 2018-02-21 20:30:00 | 83 | 2018-02-21 | 1900-01-01 20:32:00 | 128 | True | False | False | 20 | 2 |
| 2018-02-21 20:45:00 | 83 | 2018-02-21 | 1900-01-01 20:47:00 | 129 | True | False | False | 20 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-14 07:00:00 | 83 | 2022-03-14 | 1900-01-01 07:00:00 | 160 | True | False | False | 7 | 0 |
| 2022-03-14 07:15:00 | 83 | 2022-03-14 | 1900-01-01 07:15:00 | 164 | True | False | False | 7 | 0 |
| 2022-03-14 10:00:00 | 83 | 2022-03-14 | 1900-01-01 10:00:00 | 182 | True | False | False | 10 | 0 |
| 2022-03-14 10:45:00 | 83 | 2022-03-14 | 1900-01-01 10:45:00 | 168 | True | False | False | 10 | 0 |
| 2022-03-15 23:45:00 | 22 | 2022-03-15 | 1900-01-01 23:46:00 | 155 | True | False | False | 23 | 1 |
51435 rows × 9 columns
# Filtrar solo las mediciones del día 9 de junio de 2020
df_day = df_top_5_pacientes[(df_top_5_pacientes['Measurement_date'] == '2020-06-09')]
# Contar cuántas mediciones están dentro del rango y calcular el porcentaje
num_in_range = len(df_day[(df_day['Measurement'] >= 70) & (df_day['Measurement'] <= 180)])
percent_in_range = num_in_range / len(df_day) * 100
print('Porcentaje de mediciones dentro del rango: {:.2f}%'.format(percent_in_range))
Porcentaje de mediciones dentro del rango: 100.00%
**Análisis del Paciente 22**
# Filtrar los datos para el paciente 22
df_paciente_22 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 22]
# Imprimir los resultados
df_paciente_22
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-11-06 06:45:00 | 22 | 2018-11-06 | 1900-01-01 06:45:00 | 134 | True | False | False | 6 | 1 |
| 2018-11-06 07:00:00 | 22 | 2018-11-06 | 1900-01-01 07:00:00 | 134 | True | False | False | 7 | 1 |
| 2018-11-06 07:15:00 | 22 | 2018-11-06 | 1900-01-01 07:15:00 | 139 | True | False | False | 7 | 1 |
| 2018-11-06 07:30:00 | 22 | 2018-11-06 | 1900-01-01 07:30:00 | 145 | True | False | False | 7 | 1 |
| 2018-11-06 07:45:00 | 22 | 2018-11-06 | 1900-01-01 07:45:00 | 147 | True | False | False | 7 | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-08 11:30:00 | 22 | 2022-03-08 | 1900-01-01 11:30:00 | 179 | True | False | False | 11 | 1 |
| 2022-03-08 12:30:00 | 22 | 2022-03-08 | 1900-01-01 12:30:00 | 173 | True | False | False | 12 | 1 |
| 2022-03-08 12:45:00 | 22 | 2022-03-08 | 1900-01-01 12:45:00 | 171 | True | False | False | 12 | 1 |
| 2022-03-08 13:15:00 | 22 | 2022-03-08 | 1900-01-01 13:15:00 | 165 | True | False | False | 13 | 1 |
| 2022-03-15 23:45:00 | 22 | 2022-03-15 | 1900-01-01 23:46:00 | 155 | True | False | False | 23 | 1 |
10409 rows × 9 columns
Se va a calcular del mismo año y mes la glucosa media según si es entre semana o fin de semana.
Muestra el paciente con la mayor cantidad de mediciones y con su total de mediciones, además de la fecha correspondiente.
El paciente ID 22 en un mes tiene 1565 mediciones 1/2019.
# Agrupar los datos por Patient_ID y mes de Measurement_date
grouped_df = df_top_5_pacientes.groupby(['Patient_ID', df_top_5_pacientes['Measurement_date'].dt.to_period('M')])
# Encontrar el Patient_ID y la fecha (mes y año) para el paciente con la mayor cantidad de mediciones
result = grouped_df.size().idxmax()
# Calcular el total de mediciones para el paciente con la mayor cantidad de mediciones
total_measurements = grouped_df.size().max()
# Extraer el Patient_ID, año y mes del resultado
patient_id, date = result
year, month = date.year, date.month
# Imprimir los resultados
print(f'El paciente con ID {patient_id} tiene el mayor número de mediciones en un mes con un total de {total_measurements} mediciones en {month}/{year}.')
El paciente con ID 22 tiene el mayor número de mediciones en un mes con un total de 1565 mediciones en 1/2019.
# Filtrar los datos para el paciente 83 del año 2021
df_paciente_22_2019 = df_top_5_pacientes[(df_top_5_pacientes['Patient_ID'] == 22) & (df_top_5_pacientes['Measurement_date'].dt.year == 2019)]
# Imprimir los resultados
df_paciente_22_2019
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2019-01-01 00:00:00 | 22 | 2019-01-01 | 1900-01-01 00:00:00 | 194 | True | False | False | 0 | 1 |
| 2019-01-01 00:15:00 | 22 | 2019-01-01 | 1900-01-01 00:15:00 | 205 | True | False | False | 0 | 1 |
| 2019-01-01 00:30:00 | 22 | 2019-01-01 | 1900-01-01 00:30:00 | 216 | True | False | False | 0 | 1 |
| 2019-01-01 00:45:00 | 22 | 2019-01-01 | 1900-01-01 00:45:00 | 217 | True | False | False | 0 | 1 |
| 2019-01-01 01:00:00 | 22 | 2019-01-01 | 1900-01-01 01:00:00 | 225 | True | False | False | 1 | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2019-12-31 15:30:00 | 22 | 2019-12-31 | 1900-01-01 15:36:00 | 145 | True | False | False | 15 | 1 |
| 2019-12-31 15:45:00 | 22 | 2019-12-31 | 1900-01-01 15:51:00 | 140 | True | False | False | 15 | 1 |
| 2019-12-31 16:00:00 | 22 | 2019-12-31 | 1900-01-01 16:06:00 | 133 | True | False | False | 16 | 1 |
| 2019-12-31 16:15:00 | 22 | 2019-12-31 | 1900-01-01 16:21:00 | 134 | True | False | False | 16 | 1 |
| 2019-12-31 16:30:00 | 22 | 2019-12-31 | 1900-01-01 16:36:00 | 133 | True | False | False | 16 | 1 |
6099 rows × 9 columns
# pd.set_option('display.max_rows', None)
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_top_5_pacientes[(df_top_5_pacientes['Patient_ID'] == 22) & (df_top_5_pacientes['Measurement_date'].dt.year == 2019) & (df_top_5_pacientes['Measurement_date'].dt.month == 1)]
# Imprimir los resultados
df_paciente_22_1_2019
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2019-01-01 00:00:00 | 22 | 2019-01-01 | 1900-01-01 00:00:00 | 194 | True | False | False | 0 | 1 |
| 2019-01-01 00:15:00 | 22 | 2019-01-01 | 1900-01-01 00:15:00 | 205 | True | False | False | 0 | 1 |
| 2019-01-01 00:30:00 | 22 | 2019-01-01 | 1900-01-01 00:30:00 | 216 | True | False | False | 0 | 1 |
| 2019-01-01 00:45:00 | 22 | 2019-01-01 | 1900-01-01 00:45:00 | 217 | True | False | False | 0 | 1 |
| 2019-01-01 01:00:00 | 22 | 2019-01-01 | 1900-01-01 01:00:00 | 225 | True | False | False | 1 | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2019-01-31 22:45:00 | 22 | 2019-01-31 | 1900-01-01 22:52:00 | 146 | True | False | False | 22 | 3 |
| 2019-01-31 23:00:00 | 22 | 2019-01-31 | 1900-01-01 23:07:00 | 145 | True | False | False | 23 | 3 |
| 2019-01-31 23:15:00 | 22 | 2019-01-31 | 1900-01-01 23:22:00 | 144 | True | False | False | 23 | 3 |
| 2019-01-31 23:30:00 | 22 | 2019-01-31 | 1900-01-01 23:37:00 | 141 | True | False | False | 23 | 3 |
| 2019-01-31 23:45:00 | 22 | 2019-01-31 | 1900-01-01 23:52:00 | 145 | True | False | False | 23 | 3 |
1565 rows × 9 columns
# Crear una copia del dataframe df_top_5_pacientes
df_paciente_22_1_2019_copia = df_paciente_22_1_2019.copy()
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Verificar que todos los días del mes tienen mediciones
all_days_have_measurements = df_paciente_22_1_2019['Measurement_date'].nunique() == df_paciente_22_1_2019['Measurement_date'].iloc[0].days_in_month
# Verificar que no hay valores nulos en la columna Measurement
no_null_values = df_paciente_22_1_2019_copia['Measurement'].notnull().all()
# Imprimir los resultados
if all_days_have_measurements:
print('Todos los días del mes tienen mediciones.')
else:
print('No todos los días del mes tienen mediciones.')
if no_null_values:
print('No hay valores nulos en la columna Measurement.')
else:
print('Hay valores nulos en la columna Measurement.')
No todos los días del mes tienen mediciones. No hay valores nulos en la columna Measurement.
# Gráfico de barras
# Agrupar por día y contar el número de mediciones
conteo_mediciones = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.day)['Measurement'].size().reset_index()
conteo_mediciones.columns = ['Día', 'Num_Mediciones']
# Crear una figura y un eje
fig, ax = plt.subplots()
# Crear el gráfico de barras
ax.bar(conteo_mediciones['Día'], conteo_mediciones['Num_Mediciones'])
# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Número de mediciones')
# Mostrar el gráfico de barras
plt.show()
# Gráfico de líneas
# Agrupar por día y contar el número de mediciones
conteo_mediciones = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.day)['Measurement'].size().reset_index()
conteo_mediciones.columns = ['Día', 'Num_Mediciones']
# Crear una figura y un eje
fig, ax = plt.subplots()
# Crear el gráfico de líneas
ax.plot(conteo_mediciones['Día'], conteo_mediciones['Num_Mediciones'])
# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Número de mediciones')
# Mostrar el gráfico de líneas
plt.show()
El freestlye libre 2 no puede capturar valores de glucosa inferiores a 40 o superiores a 500
# Verificar si hay valores anómalos en la columna Measurement
anomalous_values = df_paciente_22_2019[(df_paciente_22_2019['Measurement'] < 40) | (df_paciente_22_2019['Measurement'] > 500)]
# Imprimir los resultados
if anomalous_values.empty:
print('No hay valores anómalos en la columna Measurement.')
else:
print('Hay valores anómalos en la columna Measurement:')
print(anomalous_values)
No hay valores anómalos en la columna Measurement.
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Filtrar solo los días de entre semana (lunes a viernes)
df_top_5_pacientes_dias = df_paciente_22_1_2019[df_paciente_22_1_2019['Measurement_date'].dt.dayofweek < 5]
# Calcular la media de la columna Measurement para los días de entre semana
mean_measurement = df_top_5_pacientes_dias['Measurement'].mean()
# Imprimir el resultado
print(f'La media de la glucosa para los días de entre semana es: {mean_measurement:.2f}')
df_top_5_pacientes_dias
La media de la glucosa para los días de entre semana es: 155.27
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2019-01-01 00:00:00 | 22 | 2019-01-01 | 1900-01-01 00:00:00 | 194 | True | False | False | 0 | 1 |
| 2019-01-01 00:15:00 | 22 | 2019-01-01 | 1900-01-01 00:15:00 | 205 | True | False | False | 0 | 1 |
| 2019-01-01 00:30:00 | 22 | 2019-01-01 | 1900-01-01 00:30:00 | 216 | True | False | False | 0 | 1 |
| 2019-01-01 00:45:00 | 22 | 2019-01-01 | 1900-01-01 00:45:00 | 217 | True | False | False | 0 | 1 |
| 2019-01-01 01:00:00 | 22 | 2019-01-01 | 1900-01-01 01:00:00 | 225 | True | False | False | 1 | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2019-01-31 22:45:00 | 22 | 2019-01-31 | 1900-01-01 22:52:00 | 146 | True | False | False | 22 | 3 |
| 2019-01-31 23:00:00 | 22 | 2019-01-31 | 1900-01-01 23:07:00 | 145 | True | False | False | 23 | 3 |
| 2019-01-31 23:15:00 | 22 | 2019-01-31 | 1900-01-01 23:22:00 | 144 | True | False | False | 23 | 3 |
| 2019-01-31 23:30:00 | 22 | 2019-01-31 | 1900-01-01 23:37:00 | 141 | True | False | False | 23 | 3 |
| 2019-01-31 23:45:00 | 22 | 2019-01-31 | 1900-01-01 23:52:00 | 145 | True | False | False | 23 | 3 |
1099 rows × 9 columns
import matplotlib.pyplot as plt
# Gráfico de barras
# Agrupar por día y calcular la media de las mediciones
mediciones_dias = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.day)['Measurement'].mean().reset_index()
mediciones_dias.columns = ['Día', 'Media_Mediciones']
# Crear una figura y un eje
fig, ax = plt.subplots()
# Crear el gráfico de barras
ax.bar(mediciones_dias['Día'], mediciones_dias['Media_Mediciones'])
# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Media de mediciones')
# Mostrar el gráfico de barras
plt.show()
# Gráfico de líneas
# Crear una figura y un eje
fig, ax = plt.subplots()
# Crear el gráfico de líneas
ax.plot(mediciones_dias['Día'], mediciones_dias['Media_Mediciones'])
# Establecer las etiquetas de los ejes
ax.set_xlabel('Día')
ax.set_ylabel('Media de mediciones')
# Mostrar el gráfico de líneas
plt.show()
# Filtrar los datos para el paciente 83 en el mes 10 del año 2021
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Filtrar solo los fines de semana (sábado y domingo)
df_top_5_pacientes_dias = df_paciente_22_1_2019[df_paciente_22_1_2019['Measurement_date'].dt.dayofweek >= 5]
# Calcular la media de la columna Measurement para los fines de semana
mean_measurement = df_top_5_pacientes_dias['Measurement'].mean()
# Imprimir el resultado
print(f'La media de la glucosa para los fin de semana es: {mean_measurement:.2f}')
df_top_5_pacientes_dias
La media de la glucosa para los fin de semana es: 165.48
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2019-01-05 00:00:00 | 22 | 2019-01-05 | 1900-01-01 00:00:00 | 216 | False | False | True | 0 | 5 |
| 2019-01-05 00:15:00 | 22 | 2019-01-05 | 1900-01-01 00:15:00 | 219 | False | False | True | 0 | 5 |
| 2019-01-05 00:30:00 | 22 | 2019-01-05 | 1900-01-01 00:30:00 | 223 | False | False | True | 0 | 5 |
| 2019-01-05 00:45:00 | 22 | 2019-01-05 | 1900-01-01 00:45:00 | 225 | False | False | True | 0 | 5 |
| 2019-01-05 01:00:00 | 22 | 2019-01-05 | 1900-01-01 01:00:00 | 190 | False | False | True | 1 | 5 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2019-01-27 22:45:00 | 22 | 2019-01-27 | 1900-01-01 22:56:00 | 251 | False | False | True | 22 | 6 |
| 2019-01-27 23:00:00 | 22 | 2019-01-27 | 1900-01-01 23:11:00 | 219 | False | False | True | 23 | 6 |
| 2019-01-27 23:15:00 | 22 | 2019-01-27 | 1900-01-01 23:26:00 | 207 | False | False | True | 23 | 6 |
| 2019-01-27 23:30:00 | 22 | 2019-01-27 | 1900-01-01 23:41:00 | 194 | False | False | True | 23 | 6 |
| 2019-01-27 23:45:00 | 22 | 2019-01-27 | 1900-01-01 23:56:00 | 176 | False | False | True | 23 | 6 |
466 rows × 9 columns
import matplotlib.pyplot as plt
# Crear un nuevo DataFrame con los valores medios de Measurement para cada día del fin de semana
mediciones_dias = df_paciente_22_1_2019[df_paciente_22_1_2019['Measurement_date'].dt.dayofweek >= 5]
mediciones_dias = mediciones_dias.groupby(mediciones_dias['Measurement_date'].dt.date)['Measurement'].mean().reset_index()
# Crear un gráfico de barras con rotación x-axis labels
plt.bar(mediciones_dias['Measurement_date'], mediciones_dias['Measurement'])
plt.title('Average Glucose Measurements on Weekends in October 2021')
plt.xlabel('Date')
plt.ylabel('Average Glucose Measurement')
plt.xticks(rotation=45) # Rotar las etiquetas del eje x en 45 grados
plt.show()
# Crear un gráfico de líneas
plt.plot(mediciones_dias['Measurement_date'], mediciones_dias['Measurement'])
plt.title('Average Glucose Measurements on Weekends in October 2021')
plt.xlabel('Date')
plt.ylabel('Average Glucose Measurement')
plt.xticks(rotation=45) # Rotar las etiquetas del eje x en 45 grados
plt.show()
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Agrupar los datos por día y contar el número de mediciones por día
daily_counts = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)['Measurement_time'].count()
# Encontrar el día con la mayor cantidad de mediciones
max_day = daily_counts.idxmax()
# Obtener la cantidad de mediciones para ese día
max_count = daily_counts.loc[max_day]
# Imprimir el resultado
print(f'El día con la mayor cantidad de mediciones es: {max_day} con {max_count} mediciones.')
El día con la mayor cantidad de mediciones es: 2019-01-05 con 96 mediciones.
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Agrupar los datos por día y contar el número de mediciones por día
daily_counts = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)['Measurement_time'].count()
# Encontrar el día con la mayor cantidad de mediciones
max_day = daily_counts.idxmax()
# Obtener la cantidad de mediciones para ese día
max_count = daily_counts.loc[max_day]
mean_count = daily_counts.mean()
print(f'La media de cantidad de mediciones por día es: {mean_count:.2f}')
# Crear un nuevo DataFrame con el conteo diario de mediciones
df_conteo_diario = pd.DataFrame({'Día': daily_counts.index, 'Cantidad de mediciones': daily_counts.values})
# Mostrar el resultado
print('\nConteo diario de mediciones:')
display(df_conteo_diario)
La media de cantidad de mediciones por día es: 53.97 Conteo diario de mediciones:
| Día | Cantidad de mediciones | |
|---|---|---|
| 0 | 2019-01-01 | 60 |
| 1 | 2019-01-02 | 75 |
| 2 | 2019-01-03 | 85 |
| 3 | 2019-01-04 | 93 |
| 4 | 2019-01-05 | 96 |
| 5 | 2019-01-06 | 76 |
| 6 | 2019-01-07 | 82 |
| 7 | 2019-01-08 | 82 |
| 8 | 2019-01-09 | 80 |
| 9 | 2019-01-10 | 24 |
| 10 | 2019-01-11 | 62 |
| 11 | 2019-01-12 | 50 |
| 12 | 2019-01-13 | 61 |
| 13 | 2019-01-15 | 61 |
| 14 | 2019-01-16 | 50 |
| 15 | 2019-01-17 | 16 |
| 16 | 2019-01-19 | 40 |
| 17 | 2019-01-20 | 66 |
| 18 | 2019-01-21 | 23 |
| 19 | 2019-01-22 | 7 |
| 20 | 2019-01-23 | 71 |
| 21 | 2019-01-24 | 3 |
| 22 | 2019-01-25 | 52 |
| 23 | 2019-01-26 | 12 |
| 24 | 2019-01-27 | 65 |
| 25 | 2019-01-28 | 61 |
| 26 | 2019-01-29 | 36 |
| 27 | 2019-01-30 | 10 |
| 28 | 2019-01-31 | 66 |
# Filtrar los datos para el paciente 22 en el día 5 del mes 1 del año 2019
df_paciente_22_5_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1) & (df_paciente_22_1_2019['Measurement_date'].dt.day == 5)]
# Calcular las mediciones tomadas (Measurement_time) para ese día
day_measurements = df_paciente_22_5_1_2019['Measurement_time'].dt.time
# Calcular el tiempo transcurrido entre cada medición
time_diff = df_paciente_22_5_1_2019['Measurement_time'].diff()
# Crear un nuevo DataFrame con las mediciones tomadas y el tiempo transcurrido entre cada medición como columnas
result = pd.DataFrame({'Measurement_time': day_measurements, 'Time_diff': time_diff})
result['Measurement'] = df_paciente_22_5_1_2019['Measurement']
# Imprimir el resultado
print(f'Las mediciones tomadas el día 5 del mes 1 del año 2019 y el tiempo transcurrido entre cada medición son:')
result
Las mediciones tomadas el día 5 del mes 1 del año 2019 y el tiempo transcurrido entre cada medición son:
| Measurement_time | Time_diff | Measurement | |
|---|---|---|---|
| Datetime | |||
| 2019-01-05 00:00:00 | 00:00:00 | NaT | 216 |
| 2019-01-05 00:15:00 | 00:15:00 | 0 days 00:15:00 | 219 |
| 2019-01-05 00:30:00 | 00:30:00 | 0 days 00:15:00 | 223 |
| 2019-01-05 00:45:00 | 00:45:00 | 0 days 00:15:00 | 225 |
| 2019-01-05 01:00:00 | 01:00:00 | 0 days 00:15:00 | 190 |
| ... | ... | ... | ... |
| 2019-01-05 22:45:00 | 22:46:00 | 0 days 00:15:00 | 224 |
| 2019-01-05 23:00:00 | 23:01:00 | 0 days 00:15:00 | 222 |
| 2019-01-05 23:15:00 | 23:16:00 | 0 days 00:15:00 | 193 |
| 2019-01-05 23:30:00 | 23:31:00 | 0 days 00:15:00 | 190 |
| 2019-01-05 23:45:00 | 23:46:00 | 0 days 00:15:00 | 189 |
96 rows × 3 columns
Muestra desde inicio del día hasta final del día, y mostrando en horizontal (X) las horas del día separadas en 2 horas y mostrando en Vertical (Y) desde 40 hasta 500 que es lo que captura el freestlye libre 2. También en la gráfica lineal representar en puntos las mediciones de glucosa (Measurement_time) tomadas, y abajo se muestra una tabla con los valores de la glucosa representados en puntos en el gráfico.
import matplotlib.pyplot as plt
from matplotlib.table import Table
# Crear una figura y un eje para la gráfica
fig1, ax1 = plt.subplots(figsize=(15, 4))
# Convertir la columna 'Measurement_time' a formato datetime y extraer solo la hora
df_paciente_22_5_1_2019 = df_paciente_22_5_1_2019.copy() # quita advertencia
df_paciente_22_5_1_2019['hour'] = pd.to_datetime(df_paciente_22_5_1_2019['Measurement_time']).dt.hour
# Crear la gráfica lineal
ax1.plot(df_paciente_22_5_1_2019['hour'], df_paciente_22_5_1_2019['Measurement'])
# Agregar puntos que representen las mediciones de glucosa tomadas. "s" para minimizar los puntos
ax1.scatter(df_paciente_22_5_1_2019['hour'], df_paciente_22_5_1_2019['Measurement'], s=10, facecolors='none', edgecolors='b')
# Establecer los límites del eje x
ax1.set_xlim([0, 24])
# Establecer los límites del eje y
ax1.set_ylim([40, 500])
# Establecer las marcas del eje x
ax1.set_xticks(range(0, 25, 2))
# Establecer las etiquetas del eje x
ax1.set_xticklabels([f'{x}:00' for x in range(0, 25, 2)])
# Agregar una cuadrícula de líneas discontinuas al gráfico
ax1.grid(True, linestyle='dashed')
# Añadir un sombreado para el tiempo en rango de 70 a 180 en el eje y
ax1.axhspan(70, 180, facecolor='green', alpha=0.2)
# Agregar un título al gráfico
ax1.set_title('Día 5/10/2021')
# Agregar etiquetas a los ejes x e y
ax1.set_xlabel('Horas')
ax1.set_ylabel('Nivel de Glucosa')
# Crear una figura y un eje para la tabla
fig2, ax2 = plt.subplots()
# Crear una tabla que muestre los valores de glucosa para cada hora del día
table_data = []
for hour in range(0, 24, 2):
measurements = df_paciente_22_5_1_2019[(df_paciente_22_5_1_2019['hour'] >= hour) & (df_paciente_22_5_1_2019['hour'] < hour + 2)]['Measurement'].values
measurements_str = '\n'.join(map(str, measurements))
table_data.append(measurements_str)
# Crear una tabla con 12 columnas en una posición específica
columns = ['0h - 2h', '2h - 4h', '4h - 6h', '6h - 8h', '8h - 10h', '10h - 12h', '12h - 14h', '14h - 16h', '16h - 18h', '18h - 20h', '20h - 22h', '22h - 24h']
table = Table(ax2, bbox=[0.059, -0.05 + 0.3, 1.9, 0.7])
#table = Table(ax2, bbox=[0.0, -0.05 + 0.3, 1.0, 0.15])
# Añadir las etiquetas de las columnas a la tabla
for i in range(len(columns)):
table.add_cell(-1, i, width=0.15, height=0.1, text=columns[i], loc='center')
# Añadir los datos a la tabla
for i in range(len(table_data)):
table.add_cell(0, i, width=0.15, height=0.9, text=table_data[i], loc='center')
ax2.add_table(table)
# Ocultar los ejes de la figura de la tabla
ax2.axis('off')
# Mostrar la gráfica y la tabla
plt.show()
Mañana: 6:00 am y termina 12:00pm.
Tarde: 12:00 pm y termina 20h.
Noche: 20h y termina 6:00 am.
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]
# Extraer las mediciones de la mañana (entre las 6:00 am y las 12:00 pm)
morning_measurements = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]['Measurement']
# Extraer las mediciones de la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_measurements = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]['Measurement']
# Extraer las mediciones de la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_measurements = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]['Measurement']
# Calcular la media de las mediciones para cada período del día
morning_mean = morning_measurements.mean()
afternoon_mean = afternoon_measurements.mean()
night_mean = night_measurements.mean()
# Crear un nuevo DataFrame con los valores de las medias para cada período del día
df_medias = pd.DataFrame({'Período del día': ['Mañana (6am-12pm)', 'Tarde (12pm-8pm)', 'Noche (8pm-6am)'],
'Media': [f'{morning_mean:.2f}', f'{afternoon_mean:.2f}', f'{night_mean:.2f}']})
# Mostrar el resultado
print('Medias:')
display(df_medias)
# Mostrar el resultado
print('\nMediciones:')
# Crear un nuevo DataFrame con las mediciones de la mañana
df_morning = pd.DataFrame({'Mañana (6am-12pm)': morning_measurements})
# Crear un nuevo DataFrame con las mediciones de la tarde
df_afternoon = pd.DataFrame({'Tarde (12pm-8pm)': afternoon_measurements})
# Crear un nuevo DataFrame con las mediciones de la noche
df_night = pd.DataFrame({'Noche (8pm-6am)': night_measurements})
# Mostrar los resultados uno al lado del otro
from IPython.display import display_html
def display_side_by_side(*args):
html_str=''
for df in args:
html_str+=df.to_html()
display_html(html_str.replace('table','table style="display:inline"'),raw=True)
display_side_by_side(df_morning, df_afternoon, df_night)
Medias:
| Período del día | Media | |
|---|---|---|
| 0 | Mañana (6am-12pm) | 148.38 |
| 1 | Tarde (12pm-8pm) | 162.91 |
| 2 | Noche (8pm-6am) | 159.98 |
Mediciones:
| Mañana (6am-12pm) | |
|---|---|
| Datetime | |
| 2019-01-02 06:00:00 | 192 |
| 2019-01-02 06:15:00 | 188 |
| 2019-01-02 06:30:00 | 187 |
| 2019-01-02 06:45:00 | 183 |
| 2019-01-02 07:00:00 | 179 |
| 2019-01-02 07:15:00 | 178 |
| 2019-01-02 07:30:00 | 176 |
| 2019-01-02 07:45:00 | 174 |
| 2019-01-02 08:00:00 | 175 |
| 2019-01-03 06:00:00 | 132 |
| 2019-01-03 06:15:00 | 129 |
| 2019-01-03 06:30:00 | 128 |
| 2019-01-03 06:45:00 | 134 |
| 2019-01-03 07:00:00 | 136 |
| 2019-01-03 07:15:00 | 132 |
| 2019-01-03 07:30:00 | 126 |
| 2019-01-03 07:45:00 | 125 |
| 2019-01-03 08:00:00 | 132 |
| 2019-01-03 08:15:00 | 140 |
| 2019-01-03 08:30:00 | 143 |
| 2019-01-03 08:45:00 | 152 |
| 2019-01-03 09:00:00 | 156 |
| 2019-01-03 09:15:00 | 159 |
| 2019-01-03 09:30:00 | 166 |
| 2019-01-03 09:45:00 | 173 |
| 2019-01-03 10:00:00 | 173 |
| 2019-01-03 10:15:00 | 169 |
| 2019-01-03 10:30:00 | 161 |
| 2019-01-03 10:45:00 | 152 |
| 2019-01-03 11:00:00 | 144 |
| 2019-01-03 11:15:00 | 134 |
| 2019-01-03 11:30:00 | 124 |
| 2019-01-03 11:45:00 | 126 |
| 2019-01-04 06:00:00 | 166 |
| 2019-01-04 06:15:00 | 162 |
| 2019-01-04 06:30:00 | 162 |
| 2019-01-04 06:45:00 | 168 |
| 2019-01-04 07:00:00 | 171 |
| 2019-01-04 07:15:00 | 165 |
| 2019-01-04 07:30:00 | 155 |
| 2019-01-04 07:45:00 | 152 |
| 2019-01-04 08:00:00 | 152 |
| 2019-01-04 08:15:00 | 151 |
| 2019-01-04 08:30:00 | 147 |
| 2019-01-04 08:45:00 | 146 |
| 2019-01-04 09:00:00 | 147 |
| 2019-01-04 09:15:00 | 152 |
| 2019-01-04 09:30:00 | 155 |
| 2019-01-04 09:45:00 | 156 |
| 2019-01-04 10:00:00 | 160 |
| 2019-01-04 10:15:00 | 141 |
| 2019-01-04 10:30:00 | 136 |
| 2019-01-04 10:45:00 | 134 |
| 2019-01-04 11:00:00 | 134 |
| 2019-01-04 11:15:00 | 132 |
| 2019-01-04 11:30:00 | 125 |
| 2019-01-04 11:45:00 | 112 |
| 2019-01-05 06:00:00 | 146 |
| 2019-01-05 06:15:00 | 141 |
| 2019-01-05 06:30:00 | 137 |
| 2019-01-05 06:45:00 | 138 |
| 2019-01-05 07:00:00 | 135 |
| 2019-01-05 07:15:00 | 137 |
| 2019-01-05 07:30:00 | 136 |
| 2019-01-05 07:45:00 | 137 |
| 2019-01-05 08:00:00 | 138 |
| 2019-01-05 08:15:00 | 139 |
| 2019-01-05 08:30:00 | 138 |
| 2019-01-05 08:45:00 | 141 |
| 2019-01-05 09:00:00 | 141 |
| 2019-01-05 09:15:00 | 142 |
| 2019-01-05 09:30:00 | 146 |
| 2019-01-05 09:45:00 | 159 |
| 2019-01-05 10:00:00 | 166 |
| 2019-01-05 10:15:00 | 163 |
| 2019-01-05 10:30:00 | 158 |
| 2019-01-05 10:45:00 | 153 |
| 2019-01-05 11:00:00 | 148 |
| 2019-01-05 11:15:00 | 143 |
| 2019-01-05 11:30:00 | 145 |
| 2019-01-05 11:45:00 | 145 |
| 2019-01-06 06:00:00 | 152 |
| 2019-01-06 06:15:00 | 150 |
| 2019-01-06 06:30:00 | 149 |
| 2019-01-06 06:45:00 | 150 |
| 2019-01-06 07:00:00 | 160 |
| 2019-01-06 09:30:00 | 134 |
| 2019-01-06 09:45:00 | 127 |
| 2019-01-06 10:45:00 | 141 |
| 2019-01-06 11:00:00 | 147 |
| 2019-01-06 11:30:00 | 160 |
| 2019-01-07 06:00:00 | 143 |
| 2019-01-07 06:15:00 | 139 |
| 2019-01-07 06:30:00 | 138 |
| 2019-01-07 06:45:00 | 139 |
| 2019-01-07 07:00:00 | 146 |
| 2019-01-07 07:15:00 | 160 |
| 2019-01-07 07:30:00 | 173 |
| 2019-01-07 07:45:00 | 170 |
| 2019-01-07 08:00:00 | 170 |
| 2019-01-07 08:15:00 | 171 |
| 2019-01-07 08:30:00 | 170 |
| 2019-01-07 08:45:00 | 166 |
| 2019-01-07 09:00:00 | 163 |
| 2019-01-07 09:15:00 | 158 |
| 2019-01-07 09:30:00 | 155 |
| 2019-01-07 09:45:00 | 160 |
| 2019-01-07 10:00:00 | 169 |
| 2019-01-07 10:15:00 | 182 |
| 2019-01-07 10:30:00 | 184 |
| 2019-01-07 10:45:00 | 178 |
| 2019-01-07 11:00:00 | 171 |
| 2019-01-07 11:15:00 | 182 |
| 2019-01-08 06:00:00 | 162 |
| 2019-01-08 06:15:00 | 161 |
| 2019-01-08 06:30:00 | 160 |
| 2019-01-08 06:45:00 | 161 |
| 2019-01-08 07:00:00 | 160 |
| 2019-01-08 07:15:00 | 157 |
| 2019-01-08 07:30:00 | 158 |
| 2019-01-08 07:45:00 | 165 |
| 2019-01-08 08:00:00 | 166 |
| 2019-01-08 08:15:00 | 162 |
| 2019-01-08 08:30:00 | 155 |
| 2019-01-08 08:45:00 | 145 |
| 2019-01-08 09:00:00 | 138 |
| 2019-01-08 09:15:00 | 137 |
| 2019-01-08 09:30:00 | 134 |
| 2019-01-08 09:45:00 | 129 |
| 2019-01-08 10:00:00 | 136 |
| 2019-01-08 10:15:00 | 133 |
| 2019-01-08 10:30:00 | 144 |
| 2019-01-08 10:45:00 | 154 |
| 2019-01-08 11:00:00 | 164 |
| 2019-01-08 11:15:00 | 168 |
| 2019-01-08 11:30:00 | 165 |
| 2019-01-08 11:45:00 | 169 |
| 2019-01-09 06:15:00 | 165 |
| 2019-01-09 06:30:00 | 176 |
| 2019-01-09 07:15:00 | 191 |
| 2019-01-09 07:30:00 | 195 |
| 2019-01-09 07:45:00 | 196 |
| 2019-01-09 08:00:00 | 192 |
| 2019-01-09 08:15:00 | 192 |
| 2019-01-09 08:30:00 | 195 |
| 2019-01-09 08:45:00 | 195 |
| 2019-01-09 09:00:00 | 193 |
| 2019-01-09 09:15:00 | 194 |
| 2019-01-09 09:30:00 | 196 |
| 2019-01-09 09:45:00 | 198 |
| 2019-01-09 10:00:00 | 199 |
| 2019-01-09 10:15:00 | 195 |
| 2019-01-09 10:30:00 | 190 |
| 2019-01-09 10:45:00 | 186 |
| 2019-01-09 11:00:00 | 183 |
| 2019-01-09 11:15:00 | 180 |
| 2019-01-09 11:30:00 | 183 |
| 2019-01-09 11:45:00 | 188 |
| 2019-01-10 11:30:00 | 139 |
| 2019-01-10 11:45:00 | 162 |
| 2019-01-11 06:00:00 | 120 |
| 2019-01-11 06:15:00 | 120 |
| 2019-01-11 06:30:00 | 124 |
| 2019-01-11 06:45:00 | 129 |
| 2019-01-11 07:00:00 | 133 |
| 2019-01-11 07:15:00 | 126 |
| 2019-01-11 07:30:00 | 124 |
| 2019-01-11 07:45:00 | 136 |
| 2019-01-11 08:00:00 | 155 |
| 2019-01-11 08:15:00 | 168 |
| 2019-01-11 08:30:00 | 177 |
| 2019-01-11 08:45:00 | 179 |
| 2019-01-11 09:00:00 | 170 |
| 2019-01-11 09:15:00 | 156 |
| 2019-01-11 09:30:00 | 148 |
| 2019-01-11 09:45:00 | 148 |
| 2019-01-11 10:00:00 | 158 |
| 2019-01-11 10:15:00 | 172 |
| 2019-01-11 10:30:00 | 178 |
| 2019-01-11 10:45:00 | 178 |
| 2019-01-11 11:00:00 | 181 |
| 2019-01-11 11:15:00 | 226 |
| 2019-01-12 09:15:00 | 105 |
| 2019-01-12 09:30:00 | 106 |
| 2019-01-12 09:45:00 | 107 |
| 2019-01-12 10:00:00 | 109 |
| 2019-01-12 10:15:00 | 100 |
| 2019-01-12 10:30:00 | 98 |
| 2019-01-12 10:45:00 | 98 |
| 2019-01-12 11:00:00 | 106 |
| 2019-01-12 11:15:00 | 121 |
| 2019-01-12 11:30:00 | 144 |
| 2019-01-12 11:45:00 | 176 |
| 2019-01-13 08:30:00 | 134 |
| 2019-01-13 09:15:00 | 145 |
| 2019-01-13 09:30:00 | 146 |
| 2019-01-13 09:45:00 | 143 |
| 2019-01-13 10:00:00 | 148 |
| 2019-01-13 10:15:00 | 168 |
| 2019-01-13 10:30:00 | 181 |
| 2019-01-13 10:45:00 | 174 |
| 2019-01-13 11:30:00 | 151 |
| 2019-01-13 11:45:00 | 157 |
| 2019-01-15 06:30:00 | 112 |
| 2019-01-15 06:45:00 | 113 |
| 2019-01-15 07:00:00 | 105 |
| 2019-01-15 07:15:00 | 107 |
| 2019-01-15 07:30:00 | 104 |
| 2019-01-15 07:45:00 | 98 |
| 2019-01-15 08:00:00 | 97 |
| 2019-01-15 08:15:00 | 101 |
| 2019-01-15 08:30:00 | 109 |
| 2019-01-15 08:45:00 | 117 |
| 2019-01-15 09:00:00 | 130 |
| 2019-01-15 09:15:00 | 147 |
| 2019-01-15 09:30:00 | 149 |
| 2019-01-15 09:45:00 | 145 |
| 2019-01-15 10:00:00 | 145 |
| 2019-01-15 10:15:00 | 147 |
| 2019-01-15 10:30:00 | 148 |
| 2019-01-15 10:45:00 | 151 |
| 2019-01-15 11:00:00 | 152 |
| 2019-01-15 11:15:00 | 150 |
| 2019-01-15 11:30:00 | 145 |
| 2019-01-15 11:45:00 | 147 |
| 2019-01-16 11:30:00 | 146 |
| 2019-01-16 11:45:00 | 149 |
| 2019-01-19 06:00:00 | 108 |
| 2019-01-19 06:15:00 | 109 |
| 2019-01-19 06:30:00 | 109 |
| 2019-01-19 06:45:00 | 106 |
| 2019-01-19 07:00:00 | 103 |
| 2019-01-19 07:15:00 | 104 |
| 2019-01-19 07:30:00 | 108 |
| 2019-01-19 07:45:00 | 104 |
| 2019-01-19 08:00:00 | 95 |
| 2019-01-19 08:15:00 | 89 |
| 2019-01-19 08:30:00 | 90 |
| 2019-01-19 08:45:00 | 98 |
| 2019-01-19 09:00:00 | 111 |
| 2019-01-19 09:15:00 | 109 |
| 2019-01-19 10:45:00 | 143 |
| 2019-01-19 11:00:00 | 145 |
| 2019-01-20 06:00:00 | 181 |
| 2019-01-20 06:15:00 | 178 |
| 2019-01-20 06:30:00 | 184 |
| 2019-01-20 06:45:00 | 195 |
| 2019-01-20 07:00:00 | 196 |
| 2019-01-20 07:15:00 | 190 |
| 2019-01-20 07:30:00 | 187 |
| 2019-01-20 07:45:00 | 183 |
| 2019-01-20 08:00:00 | 177 |
| 2019-01-20 08:15:00 | 173 |
| 2019-01-20 08:30:00 | 171 |
| 2019-01-20 08:45:00 | 169 |
| 2019-01-20 09:00:00 | 170 |
| 2019-01-20 09:15:00 | 168 |
| 2019-01-20 09:30:00 | 164 |
| 2019-01-20 09:45:00 | 171 |
| 2019-01-20 10:00:00 | 173 |
| 2019-01-20 10:15:00 | 165 |
| 2019-01-20 10:30:00 | 162 |
| 2019-01-20 10:45:00 | 170 |
| 2019-01-20 11:15:00 | 185 |
| 2019-01-20 11:30:00 | 176 |
| 2019-01-20 11:45:00 | 165 |
| 2019-01-23 06:00:00 | 110 |
| 2019-01-23 06:15:00 | 109 |
| 2019-01-23 06:30:00 | 109 |
| 2019-01-23 06:45:00 | 114 |
| 2019-01-23 07:00:00 | 127 |
| 2019-01-23 07:15:00 | 135 |
| 2019-01-23 07:30:00 | 136 |
| 2019-01-23 07:45:00 | 138 |
| 2019-01-23 08:00:00 | 148 |
| 2019-01-23 08:15:00 | 155 |
| 2019-01-23 08:30:00 | 150 |
| 2019-01-23 08:45:00 | 145 |
| 2019-01-23 09:00:00 | 141 |
| 2019-01-23 09:15:00 | 136 |
| 2019-01-23 09:30:00 | 129 |
| 2019-01-23 09:45:00 | 126 |
| 2019-01-23 10:00:00 | 128 |
| 2019-01-23 10:15:00 | 128 |
| 2019-01-23 10:30:00 | 125 |
| 2019-01-23 10:45:00 | 131 |
| 2019-01-23 11:00:00 | 149 |
| 2019-01-23 11:15:00 | 167 |
| 2019-01-23 11:30:00 | 177 |
| 2019-01-23 11:45:00 | 189 |
| 2019-01-25 11:00:00 | 129 |
| 2019-01-25 11:15:00 | 133 |
| 2019-01-25 11:30:00 | 145 |
| 2019-01-25 11:45:00 | 163 |
| 2019-01-27 06:00:00 | 154 |
| 2019-01-27 06:15:00 | 157 |
| 2019-01-27 06:30:00 | 160 |
| 2019-01-27 06:45:00 | 150 |
| 2019-01-27 07:00:00 | 145 |
| 2019-01-27 07:15:00 | 147 |
| 2019-01-27 07:30:00 | 148 |
| 2019-01-27 07:45:00 | 145 |
| 2019-01-27 08:00:00 | 152 |
| 2019-01-27 08:15:00 | 163 |
| 2019-01-27 08:30:00 | 169 |
| 2019-01-27 08:45:00 | 169 |
| 2019-01-27 09:00:00 | 168 |
| 2019-01-27 09:15:00 | 160 |
| 2019-01-27 09:30:00 | 154 |
| 2019-01-27 09:45:00 | 146 |
| 2019-01-27 10:00:00 | 137 |
| 2019-01-27 10:15:00 | 127 |
| 2019-01-27 10:30:00 | 121 |
| 2019-01-27 10:45:00 | 138 |
| 2019-01-27 11:00:00 | 150 |
| 2019-01-27 11:15:00 | 177 |
| 2019-01-27 11:30:00 | 206 |
| 2019-01-27 11:45:00 | 222 |
| 2019-01-28 06:00:00 | 79 |
| 2019-01-28 06:15:00 | 79 |
| 2019-01-28 06:30:00 | 80 |
| 2019-01-28 06:45:00 | 85 |
| 2019-01-28 07:00:00 | 88 |
| 2019-01-28 07:15:00 | 97 |
| 2019-01-28 07:30:00 | 104 |
| 2019-01-28 07:45:00 | 103 |
| 2019-01-29 06:00:00 | 138 |
| 2019-01-29 06:45:00 | 146 |
| 2019-01-29 07:15:00 | 138 |
| 2019-01-29 07:30:00 | 139 |
| 2019-01-29 07:45:00 | 141 |
| 2019-01-29 08:00:00 | 143 |
| 2019-01-29 08:15:00 | 133 |
| 2019-01-29 08:30:00 | 119 |
| 2019-01-29 09:15:00 | 95 |
| 2019-01-29 09:30:00 | 104 |
| 2019-01-29 09:45:00 | 118 |
| 2019-01-29 10:00:00 | 130 |
| 2019-01-29 10:15:00 | 140 |
| 2019-01-29 10:30:00 | 152 |
| 2019-01-31 06:00:00 | 121 |
| 2019-01-31 06:15:00 | 120 |
| 2019-01-31 06:30:00 | 116 |
| 2019-01-31 06:45:00 | 122 |
| 2019-01-31 07:00:00 | 156 |
| 2019-01-31 07:15:00 | 161 |
| 2019-01-31 07:30:00 | 167 |
| 2019-01-31 08:30:00 | 202 |
| 2019-01-31 08:45:00 | 195 |
| 2019-01-31 09:00:00 | 187 |
| 2019-01-31 10:00:00 | 148 |
| 2019-01-31 10:15:00 | 141 |
| 2019-01-31 10:30:00 | 131 |
| 2019-01-31 10:45:00 | 119 |
| 2019-01-31 11:30:00 | 127 |
| Tarde (12pm-8pm) | |
|---|---|
| Datetime | |
| 2019-01-01 12:30:00 | 153 |
| 2019-01-01 12:45:00 | 144 |
| 2019-01-01 13:00:00 | 143 |
| 2019-01-01 13:15:00 | 148 |
| 2019-01-01 13:30:00 | 144 |
| 2019-01-01 13:45:00 | 140 |
| 2019-01-01 14:00:00 | 139 |
| 2019-01-01 14:15:00 | 137 |
| 2019-01-01 14:30:00 | 132 |
| 2019-01-01 14:45:00 | 137 |
| 2019-01-01 15:00:00 | 149 |
| 2019-01-01 15:15:00 | 160 |
| 2019-01-01 15:30:00 | 158 |
| 2019-01-01 15:45:00 | 146 |
| 2019-01-01 16:00:00 | 142 |
| 2019-01-01 16:15:00 | 147 |
| 2019-01-01 16:30:00 | 146 |
| 2019-01-01 16:45:00 | 148 |
| 2019-01-01 17:00:00 | 151 |
| 2019-01-01 17:15:00 | 147 |
| 2019-01-01 17:30:00 | 145 |
| 2019-01-01 17:45:00 | 124 |
| 2019-01-01 18:00:00 | 125 |
| 2019-01-01 18:15:00 | 127 |
| 2019-01-01 18:30:00 | 130 |
| 2019-01-01 18:45:00 | 132 |
| 2019-01-01 19:00:00 | 131 |
| 2019-01-01 19:15:00 | 128 |
| 2019-01-01 19:30:00 | 123 |
| 2019-01-01 19:45:00 | 117 |
| 2019-01-02 13:15:00 | 130 |
| 2019-01-02 13:30:00 | 124 |
| 2019-01-02 13:45:00 | 120 |
| 2019-01-02 14:00:00 | 115 |
| 2019-01-02 14:15:00 | 120 |
| 2019-01-02 14:30:00 | 131 |
| 2019-01-02 14:45:00 | 140 |
| 2019-01-02 15:00:00 | 142 |
| 2019-01-02 15:15:00 | 143 |
| 2019-01-02 15:30:00 | 146 |
| 2019-01-02 15:45:00 | 152 |
| 2019-01-02 16:00:00 | 153 |
| 2019-01-02 16:15:00 | 153 |
| 2019-01-02 16:30:00 | 158 |
| 2019-01-02 16:45:00 | 162 |
| 2019-01-02 17:00:00 | 175 |
| 2019-01-02 17:15:00 | 184 |
| 2019-01-02 17:30:00 | 190 |
| 2019-01-02 17:45:00 | 191 |
| 2019-01-02 18:00:00 | 188 |
| 2019-01-02 18:15:00 | 181 |
| 2019-01-02 18:30:00 | 177 |
| 2019-01-02 18:45:00 | 167 |
| 2019-01-02 19:00:00 | 157 |
| 2019-01-02 19:15:00 | 155 |
| 2019-01-02 19:30:00 | 155 |
| 2019-01-02 19:45:00 | 155 |
| 2019-01-03 12:00:00 | 136 |
| 2019-01-03 12:15:00 | 140 |
| 2019-01-03 12:30:00 | 147 |
| 2019-01-03 12:45:00 | 157 |
| 2019-01-03 13:00:00 | 163 |
| 2019-01-03 13:15:00 | 162 |
| 2019-01-03 13:30:00 | 163 |
| 2019-01-03 13:45:00 | 159 |
| 2019-01-03 14:00:00 | 141 |
| 2019-01-03 14:15:00 | 131 |
| 2019-01-03 14:30:00 | 124 |
| 2019-01-03 14:45:00 | 122 |
| 2019-01-03 15:00:00 | 124 |
| 2019-01-03 15:15:00 | 127 |
| 2019-01-03 15:30:00 | 138 |
| 2019-01-03 15:45:00 | 149 |
| 2019-01-03 16:00:00 | 168 |
| 2019-01-03 16:15:00 | 165 |
| 2019-01-03 16:30:00 | 158 |
| 2019-01-03 16:45:00 | 154 |
| 2019-01-03 17:00:00 | 154 |
| 2019-01-03 17:15:00 | 157 |
| 2019-01-03 17:30:00 | 158 |
| 2019-01-03 17:45:00 | 153 |
| 2019-01-03 18:00:00 | 148 |
| 2019-01-03 18:15:00 | 147 |
| 2019-01-03 18:30:00 | 152 |
| 2019-01-03 18:45:00 | 156 |
| 2019-01-03 19:00:00 | 162 |
| 2019-01-03 19:15:00 | 168 |
| 2019-01-03 19:30:00 | 171 |
| 2019-01-03 19:45:00 | 174 |
| 2019-01-04 12:00:00 | 108 |
| 2019-01-04 12:15:00 | 109 |
| 2019-01-04 12:30:00 | 111 |
| 2019-01-04 12:45:00 | 110 |
| 2019-01-04 13:00:00 | 108 |
| 2019-01-04 13:15:00 | 130 |
| 2019-01-04 13:30:00 | 128 |
| 2019-01-04 13:45:00 | 124 |
| 2019-01-04 14:00:00 | 120 |
| 2019-01-04 14:15:00 | 119 |
| 2019-01-04 14:30:00 | 121 |
| 2019-01-04 14:45:00 | 129 |
| 2019-01-04 15:00:00 | 148 |
| 2019-01-04 15:15:00 | 161 |
| 2019-01-04 15:30:00 | 164 |
| 2019-01-04 15:45:00 | 165 |
| 2019-01-04 16:00:00 | 175 |
| 2019-01-04 16:15:00 | 188 |
| 2019-01-04 16:30:00 | 200 |
| 2019-01-04 16:45:00 | 198 |
| 2019-01-04 17:00:00 | 200 |
| 2019-01-04 17:15:00 | 195 |
| 2019-01-04 17:30:00 | 202 |
| 2019-01-04 17:45:00 | 215 |
| 2019-01-04 18:00:00 | 218 |
| 2019-01-04 18:15:00 | 214 |
| 2019-01-04 18:30:00 | 213 |
| 2019-01-04 18:45:00 | 213 |
| 2019-01-04 19:00:00 | 192 |
| 2019-01-04 19:15:00 | 188 |
| 2019-01-04 19:30:00 | 178 |
| 2019-01-04 19:45:00 | 174 |
| 2019-01-05 12:00:00 | 142 |
| 2019-01-05 12:15:00 | 140 |
| 2019-01-05 12:30:00 | 143 |
| 2019-01-05 12:45:00 | 146 |
| 2019-01-05 13:00:00 | 154 |
| 2019-01-05 13:15:00 | 160 |
| 2019-01-05 13:30:00 | 158 |
| 2019-01-05 13:45:00 | 149 |
| 2019-01-05 14:00:00 | 138 |
| 2019-01-05 14:15:00 | 131 |
| 2019-01-05 14:30:00 | 125 |
| 2019-01-05 14:45:00 | 106 |
| 2019-01-05 15:00:00 | 124 |
| 2019-01-05 15:15:00 | 145 |
| 2019-01-05 15:30:00 | 161 |
| 2019-01-05 15:45:00 | 162 |
| 2019-01-05 16:00:00 | 168 |
| 2019-01-05 16:15:00 | 172 |
| 2019-01-05 16:30:00 | 173 |
| 2019-01-05 16:45:00 | 174 |
| 2019-01-05 17:00:00 | 174 |
| 2019-01-05 17:15:00 | 167 |
| 2019-01-05 17:30:00 | 159 |
| 2019-01-05 17:45:00 | 157 |
| 2019-01-05 18:00:00 | 152 |
| 2019-01-05 18:15:00 | 148 |
| 2019-01-05 18:30:00 | 149 |
| 2019-01-05 18:45:00 | 153 |
| 2019-01-05 19:00:00 | 158 |
| 2019-01-05 19:15:00 | 162 |
| 2019-01-05 19:30:00 | 193 |
| 2019-01-05 19:45:00 | 195 |
| 2019-01-06 12:15:00 | 120 |
| 2019-01-06 12:30:00 | 118 |
| 2019-01-06 14:00:00 | 128 |
| 2019-01-06 14:15:00 | 146 |
| 2019-01-06 14:30:00 | 156 |
| 2019-01-06 14:45:00 | 166 |
| 2019-01-06 15:00:00 | 185 |
| 2019-01-06 15:15:00 | 203 |
| 2019-01-06 15:30:00 | 262 |
| 2019-01-06 15:45:00 | 219 |
| 2019-01-06 16:00:00 | 218 |
| 2019-01-06 16:15:00 | 226 |
| 2019-01-06 16:30:00 | 235 |
| 2019-01-06 16:45:00 | 237 |
| 2019-01-06 17:00:00 | 237 |
| 2019-01-06 17:15:00 | 232 |
| 2019-01-06 17:30:00 | 226 |
| 2019-01-06 17:45:00 | 224 |
| 2019-01-06 18:00:00 | 228 |
| 2019-01-06 18:15:00 | 236 |
| 2019-01-06 18:30:00 | 244 |
| 2019-01-06 18:45:00 | 246 |
| 2019-01-06 19:00:00 | 249 |
| 2019-01-06 19:15:00 | 254 |
| 2019-01-06 19:30:00 | 255 |
| 2019-01-06 19:45:00 | 252 |
| 2019-01-07 13:30:00 | 140 |
| 2019-01-07 13:45:00 | 137 |
| 2019-01-07 14:00:00 | 133 |
| 2019-01-07 14:15:00 | 136 |
| 2019-01-07 15:15:00 | 165 |
| 2019-01-07 15:30:00 | 170 |
| 2019-01-07 15:45:00 | 169 |
| 2019-01-07 16:00:00 | 164 |
| 2019-01-07 17:00:00 | 136 |
| 2019-01-07 17:15:00 | 150 |
| 2019-01-07 17:30:00 | 157 |
| 2019-01-07 17:45:00 | 161 |
| 2019-01-07 18:00:00 | 167 |
| 2019-01-07 18:15:00 | 172 |
| 2019-01-07 18:30:00 | 179 |
| 2019-01-07 18:45:00 | 188 |
| 2019-01-07 19:00:00 | 191 |
| 2019-01-07 19:15:00 | 184 |
| 2019-01-07 19:30:00 | 181 |
| 2019-01-07 19:45:00 | 181 |
| 2019-01-08 12:00:00 | 175 |
| 2019-01-08 12:15:00 | 200 |
| 2019-01-08 12:30:00 | 186 |
| 2019-01-08 13:15:00 | 173 |
| 2019-01-08 13:30:00 | 167 |
| 2019-01-08 14:15:00 | 148 |
| 2019-01-08 14:45:00 | 134 |
| 2019-01-08 15:30:00 | 198 |
| 2019-01-08 15:45:00 | 199 |
| 2019-01-08 16:30:00 | 189 |
| 2019-01-08 16:45:00 | 189 |
| 2019-01-08 17:30:00 | 142 |
| 2019-01-08 17:45:00 | 133 |
| 2019-01-08 18:00:00 | 128 |
| 2019-01-08 18:15:00 | 126 |
| 2019-01-08 18:30:00 | 130 |
| 2019-01-08 18:45:00 | 136 |
| 2019-01-08 19:00:00 | 142 |
| 2019-01-08 19:15:00 | 146 |
| 2019-01-09 12:00:00 | 192 |
| 2019-01-09 12:15:00 | 200 |
| 2019-01-09 12:30:00 | 214 |
| 2019-01-09 12:45:00 | 218 |
| 2019-01-09 13:00:00 | 209 |
| 2019-01-09 13:15:00 | 200 |
| 2019-01-09 13:30:00 | 192 |
| 2019-01-09 13:45:00 | 182 |
| 2019-01-09 14:00:00 | 176 |
| 2019-01-09 14:15:00 | 181 |
| 2019-01-09 14:30:00 | 190 |
| 2019-01-09 14:45:00 | 196 |
| 2019-01-09 15:00:00 | 209 |
| 2019-01-09 15:15:00 | 204 |
| 2019-01-09 15:30:00 | 209 |
| 2019-01-09 15:45:00 | 211 |
| 2019-01-09 16:30:00 | 181 |
| 2019-01-09 16:45:00 | 171 |
| 2019-01-09 17:30:00 | 147 |
| 2019-01-09 17:45:00 | 150 |
| 2019-01-09 18:15:00 | 168 |
| 2019-01-09 19:15:00 | 216 |
| 2019-01-09 19:45:00 | 214 |
| 2019-01-10 12:00:00 | 170 |
| 2019-01-10 12:15:00 | 180 |
| 2019-01-10 12:30:00 | 183 |
| 2019-01-10 12:45:00 | 178 |
| 2019-01-10 13:00:00 | 174 |
| 2019-01-10 13:15:00 | 173 |
| 2019-01-10 13:30:00 | 173 |
| 2019-01-10 13:45:00 | 169 |
| 2019-01-10 14:00:00 | 184 |
| 2019-01-10 14:15:00 | 153 |
| 2019-01-10 14:45:00 | 140 |
| 2019-01-11 18:00:00 | 151 |
| 2019-01-11 18:15:00 | 160 |
| 2019-01-11 18:30:00 | 160 |
| 2019-01-11 18:45:00 | 153 |
| 2019-01-12 12:00:00 | 195 |
| 2019-01-12 12:15:00 | 192 |
| 2019-01-12 12:30:00 | 184 |
| 2019-01-12 12:45:00 | 172 |
| 2019-01-12 13:00:00 | 158 |
| 2019-01-12 13:15:00 | 148 |
| 2019-01-12 13:30:00 | 142 |
| 2019-01-12 13:45:00 | 137 |
| 2019-01-12 14:00:00 | 132 |
| 2019-01-12 14:15:00 | 118 |
| 2019-01-12 14:30:00 | 108 |
| 2019-01-12 14:45:00 | 112 |
| 2019-01-12 15:00:00 | 124 |
| 2019-01-12 15:15:00 | 130 |
| 2019-01-12 15:30:00 | 143 |
| 2019-01-12 15:45:00 | 174 |
| 2019-01-12 16:00:00 | 201 |
| 2019-01-12 16:15:00 | 205 |
| 2019-01-12 16:30:00 | 206 |
| 2019-01-12 16:45:00 | 206 |
| 2019-01-12 17:00:00 | 201 |
| 2019-01-12 17:15:00 | 191 |
| 2019-01-12 17:30:00 | 182 |
| 2019-01-12 17:45:00 | 175 |
| 2019-01-12 18:00:00 | 170 |
| 2019-01-12 18:15:00 | 166 |
| 2019-01-12 18:30:00 | 169 |
| 2019-01-12 18:45:00 | 180 |
| 2019-01-12 19:00:00 | 195 |
| 2019-01-13 12:30:00 | 159 |
| 2019-01-13 12:45:00 | 156 |
| 2019-01-13 13:00:00 | 150 |
| 2019-01-13 13:15:00 | 148 |
| 2019-01-13 13:30:00 | 151 |
| 2019-01-13 13:45:00 | 160 |
| 2019-01-13 14:00:00 | 173 |
| 2019-01-13 14:15:00 | 184 |
| 2019-01-13 14:30:00 | 188 |
| 2019-01-13 14:45:00 | 196 |
| 2019-01-13 15:00:00 | 207 |
| 2019-01-13 15:15:00 | 216 |
| 2019-01-13 15:30:00 | 223 |
| 2019-01-13 15:45:00 | 230 |
| 2019-01-13 16:00:00 | 238 |
| 2019-01-13 16:15:00 | 241 |
| 2019-01-13 16:30:00 | 242 |
| 2019-01-13 16:45:00 | 237 |
| 2019-01-13 17:00:00 | 228 |
| 2019-01-13 17:15:00 | 221 |
| 2019-01-13 17:30:00 | 213 |
| 2019-01-13 17:45:00 | 205 |
| 2019-01-13 18:00:00 | 199 |
| 2019-01-13 18:15:00 | 193 |
| 2019-01-13 18:30:00 | 185 |
| 2019-01-13 18:45:00 | 173 |
| 2019-01-13 19:00:00 | 165 |
| 2019-01-13 19:15:00 | 161 |
| 2019-01-13 19:30:00 | 152 |
| 2019-01-13 19:45:00 | 146 |
| 2019-01-15 12:00:00 | 155 |
| 2019-01-15 12:15:00 | 160 |
| 2019-01-15 12:30:00 | 164 |
| 2019-01-15 12:45:00 | 164 |
| 2019-01-15 13:00:00 | 157 |
| 2019-01-15 13:15:00 | 154 |
| 2019-01-15 13:30:00 | 154 |
| 2019-01-15 13:45:00 | 151 |
| 2019-01-15 14:00:00 | 144 |
| 2019-01-15 14:30:00 | 167 |
| 2019-01-15 14:45:00 | 152 |
| 2019-01-15 16:00:00 | 152 |
| 2019-01-15 16:30:00 | 128 |
| 2019-01-15 16:45:00 | 123 |
| 2019-01-15 17:00:00 | 116 |
| 2019-01-15 17:15:00 | 111 |
| 2019-01-15 17:30:00 | 105 |
| 2019-01-15 17:45:00 | 100 |
| 2019-01-15 18:00:00 | 101 |
| 2019-01-15 18:15:00 | 105 |
| 2019-01-15 18:30:00 | 105 |
| 2019-01-15 18:45:00 | 104 |
| 2019-01-15 19:00:00 | 104 |
| 2019-01-15 19:15:00 | 102 |
| 2019-01-15 19:30:00 | 101 |
| 2019-01-15 19:45:00 | 103 |
| 2019-01-16 12:00:00 | 154 |
| 2019-01-16 12:15:00 | 154 |
| 2019-01-16 12:30:00 | 154 |
| 2019-01-16 12:45:00 | 167 |
| 2019-01-16 13:00:00 | 173 |
| 2019-01-16 13:15:00 | 176 |
| 2019-01-16 13:30:00 | 188 |
| 2019-01-16 13:45:00 | 183 |
| 2019-01-16 14:00:00 | 170 |
| 2019-01-16 14:15:00 | 164 |
| 2019-01-16 14:30:00 | 138 |
| 2019-01-16 14:45:00 | 133 |
| 2019-01-16 15:00:00 | 132 |
| 2019-01-16 15:15:00 | 139 |
| 2019-01-16 15:30:00 | 143 |
| 2019-01-16 15:45:00 | 136 |
| 2019-01-16 16:00:00 | 126 |
| 2019-01-16 16:15:00 | 116 |
| 2019-01-16 16:30:00 | 108 |
| 2019-01-16 16:45:00 | 103 |
| 2019-01-16 17:00:00 | 102 |
| 2019-01-16 17:15:00 | 104 |
| 2019-01-16 17:30:00 | 106 |
| 2019-01-16 17:45:00 | 112 |
| 2019-01-16 18:00:00 | 119 |
| 2019-01-16 18:15:00 | 128 |
| 2019-01-16 18:30:00 | 142 |
| 2019-01-16 18:45:00 | 153 |
| 2019-01-16 19:00:00 | 158 |
| 2019-01-16 19:15:00 | 158 |
| 2019-01-16 19:30:00 | 156 |
| 2019-01-16 19:45:00 | 160 |
| 2019-01-19 12:30:00 | 176 |
| 2019-01-19 12:45:00 | 172 |
| 2019-01-19 13:15:00 | 161 |
| 2019-01-19 13:30:00 | 149 |
| 2019-01-19 13:45:00 | 140 |
| 2019-01-20 12:00:00 | 160 |
| 2019-01-20 12:15:00 | 158 |
| 2019-01-20 12:30:00 | 152 |
| 2019-01-20 12:45:00 | 145 |
| 2019-01-20 13:00:00 | 139 |
| 2019-01-20 13:15:00 | 136 |
| 2019-01-20 13:30:00 | 135 |
| 2019-01-20 13:45:00 | 138 |
| 2019-01-20 14:00:00 | 138 |
| 2019-01-20 14:15:00 | 132 |
| 2019-01-20 14:30:00 | 120 |
| 2019-01-20 14:45:00 | 116 |
| 2019-01-20 16:15:00 | 144 |
| 2019-01-20 16:30:00 | 166 |
| 2019-01-20 16:45:00 | 171 |
| 2019-01-20 17:00:00 | 174 |
| 2019-01-20 17:15:00 | 178 |
| 2019-01-20 17:30:00 | 184 |
| 2019-01-20 17:45:00 | 187 |
| 2019-01-20 18:00:00 | 188 |
| 2019-01-20 18:15:00 | 194 |
| 2019-01-20 18:30:00 | 207 |
| 2019-01-20 18:45:00 | 223 |
| 2019-01-20 19:00:00 | 232 |
| 2019-01-20 19:15:00 | 228 |
| 2019-01-20 19:30:00 | 223 |
| 2019-01-20 19:45:00 | 217 |
| 2019-01-21 17:30:00 | 190 |
| 2019-01-21 17:45:00 | 186 |
| 2019-01-21 18:00:00 | 183 |
| 2019-01-21 18:15:00 | 191 |
| 2019-01-21 18:30:00 | 197 |
| 2019-01-21 18:45:00 | 202 |
| 2019-01-21 19:00:00 | 204 |
| 2019-01-21 19:15:00 | 182 |
| 2019-01-21 19:30:00 | 182 |
| 2019-01-21 19:45:00 | 176 |
| 2019-01-23 12:00:00 | 199 |
| 2019-01-23 12:15:00 | 202 |
| 2019-01-23 12:30:00 | 201 |
| 2019-01-23 12:45:00 | 195 |
| 2019-01-23 13:00:00 | 184 |
| 2019-01-23 13:15:00 | 175 |
| 2019-01-23 13:30:00 | 175 |
| 2019-01-23 13:45:00 | 174 |
| 2019-01-23 14:00:00 | 170 |
| 2019-01-23 14:15:00 | 171 |
| 2019-01-23 14:30:00 | 172 |
| 2019-01-23 14:45:00 | 177 |
| 2019-01-23 15:00:00 | 169 |
| 2019-01-23 15:15:00 | 173 |
| 2019-01-23 15:30:00 | 173 |
| 2019-01-23 15:45:00 | 174 |
| 2019-01-23 16:00:00 | 171 |
| 2019-01-23 16:15:00 | 160 |
| 2019-01-23 16:30:00 | 149 |
| 2019-01-23 16:45:00 | 138 |
| 2019-01-23 17:00:00 | 132 |
| 2019-01-25 12:00:00 | 180 |
| 2019-01-25 12:15:00 | 198 |
| 2019-01-25 12:30:00 | 210 |
| 2019-01-25 12:45:00 | 209 |
| 2019-01-25 13:00:00 | 203 |
| 2019-01-25 13:15:00 | 201 |
| 2019-01-25 13:30:00 | 201 |
| 2019-01-25 13:45:00 | 205 |
| 2019-01-25 14:00:00 | 207 |
| 2019-01-25 14:15:00 | 200 |
| 2019-01-25 14:30:00 | 193 |
| 2019-01-25 14:45:00 | 192 |
| 2019-01-25 15:00:00 | 191 |
| 2019-01-25 15:15:00 | 188 |
| 2019-01-25 15:30:00 | 182 |
| 2019-01-25 15:45:00 | 173 |
| 2019-01-25 16:00:00 | 161 |
| 2019-01-25 16:15:00 | 141 |
| 2019-01-25 16:30:00 | 114 |
| 2019-01-25 16:45:00 | 94 |
| 2019-01-25 17:00:00 | 86 |
| 2019-01-25 17:15:00 | 80 |
| 2019-01-25 17:30:00 | 82 |
| 2019-01-25 17:45:00 | 91 |
| 2019-01-25 18:00:00 | 97 |
| 2019-01-25 18:15:00 | 103 |
| 2019-01-25 18:30:00 | 115 |
| 2019-01-25 18:45:00 | 133 |
| 2019-01-25 19:00:00 | 144 |
| 2019-01-25 19:15:00 | 148 |
| 2019-01-25 19:30:00 | 148 |
| 2019-01-25 19:45:00 | 151 |
| 2019-01-27 12:00:00 | 229 |
| 2019-01-27 12:15:00 | 236 |
| 2019-01-27 12:30:00 | 231 |
| 2019-01-27 12:45:00 | 220 |
| 2019-01-27 13:00:00 | 210 |
| 2019-01-27 13:15:00 | 199 |
| 2019-01-27 13:30:00 | 184 |
| 2019-01-27 13:45:00 | 169 |
| 2019-01-27 14:00:00 | 157 |
| 2019-01-27 14:15:00 | 148 |
| 2019-01-27 14:30:00 | 141 |
| 2019-01-27 14:45:00 | 140 |
| 2019-01-28 14:45:00 | 175 |
| 2019-01-28 15:00:00 | 172 |
| 2019-01-28 15:15:00 | 166 |
| 2019-01-28 15:45:00 | 162 |
| 2019-01-28 16:00:00 | 158 |
| 2019-01-28 16:30:00 | 138 |
| 2019-01-28 16:45:00 | 124 |
| 2019-01-28 17:00:00 | 114 |
| 2019-01-28 17:15:00 | 114 |
| 2019-01-28 17:30:00 | 116 |
| 2019-01-28 17:45:00 | 113 |
| 2019-01-28 18:00:00 | 114 |
| 2019-01-28 18:15:00 | 120 |
| 2019-01-28 18:30:00 | 127 |
| 2019-01-28 18:45:00 | 132 |
| 2019-01-28 19:00:00 | 146 |
| 2019-01-28 19:15:00 | 167 |
| 2019-01-28 19:30:00 | 169 |
| 2019-01-28 19:45:00 | 162 |
| 2019-01-31 12:00:00 | 135 |
| 2019-01-31 12:15:00 | 135 |
| 2019-01-31 12:30:00 | 135 |
| 2019-01-31 12:45:00 | 138 |
| 2019-01-31 13:00:00 | 142 |
| 2019-01-31 13:15:00 | 139 |
| 2019-01-31 13:30:00 | 135 |
| 2019-01-31 13:45:00 | 132 |
| 2019-01-31 14:00:00 | 134 |
| 2019-01-31 14:45:00 | 180 |
| 2019-01-31 15:30:00 | 175 |
| 2019-01-31 15:45:00 | 176 |
| 2019-01-31 16:00:00 | 189 |
| 2019-01-31 16:15:00 | 192 |
| 2019-01-31 19:45:00 | 145 |
| Noche (8pm-6am) | |
|---|---|
| Datetime | |
| 2019-01-01 00:00:00 | 194 |
| 2019-01-01 00:15:00 | 205 |
| 2019-01-01 00:30:00 | 216 |
| 2019-01-01 00:45:00 | 217 |
| 2019-01-01 01:00:00 | 225 |
| 2019-01-01 01:15:00 | 233 |
| 2019-01-01 01:30:00 | 236 |
| 2019-01-01 01:45:00 | 231 |
| 2019-01-01 02:00:00 | 230 |
| 2019-01-01 02:15:00 | 226 |
| 2019-01-01 02:30:00 | 217 |
| 2019-01-01 02:45:00 | 214 |
| 2019-01-01 03:00:00 | 216 |
| 2019-01-01 03:15:00 | 215 |
| 2019-01-01 03:30:00 | 210 |
| 2019-01-01 03:45:00 | 185 |
| 2019-01-01 20:00:00 | 116 |
| 2019-01-01 20:15:00 | 121 |
| 2019-01-01 20:30:00 | 128 |
| 2019-01-01 20:45:00 | 135 |
| 2019-01-01 21:00:00 | 138 |
| 2019-01-01 21:15:00 | 136 |
| 2019-01-01 21:30:00 | 132 |
| 2019-01-01 21:45:00 | 133 |
| 2019-01-01 22:00:00 | 128 |
| 2019-01-01 22:15:00 | 110 |
| 2019-01-01 22:30:00 | 116 |
| 2019-01-01 23:15:00 | 111 |
| 2019-01-01 23:30:00 | 109 |
| 2019-01-01 23:45:00 | 107 |
| 2019-01-02 00:00:00 | 107 |
| 2019-01-02 00:15:00 | 113 |
| 2019-01-02 00:30:00 | 124 |
| 2019-01-02 01:00:00 | 131 |
| 2019-01-02 01:15:00 | 117 |
| 2019-01-02 01:30:00 | 132 |
| 2019-01-02 01:45:00 | 130 |
| 2019-01-02 02:00:00 | 133 |
| 2019-01-02 02:15:00 | 135 |
| 2019-01-02 02:30:00 | 137 |
| 2019-01-02 02:45:00 | 138 |
| 2019-01-02 03:00:00 | 139 |
| 2019-01-02 03:15:00 | 155 |
| 2019-01-02 03:30:00 | 182 |
| 2019-01-02 03:45:00 | 190 |
| 2019-01-02 04:00:00 | 195 |
| 2019-01-02 04:15:00 | 201 |
| 2019-01-02 04:30:00 | 202 |
| 2019-01-02 04:45:00 | 195 |
| 2019-01-02 05:00:00 | 192 |
| 2019-01-02 05:15:00 | 197 |
| 2019-01-02 05:30:00 | 199 |
| 2019-01-02 05:45:00 | 198 |
| 2019-01-02 20:00:00 | 157 |
| 2019-01-02 20:15:00 | 158 |
| 2019-01-02 20:30:00 | 156 |
| 2019-01-02 20:45:00 | 155 |
| 2019-01-02 21:00:00 | 153 |
| 2019-01-02 21:15:00 | 152 |
| 2019-01-02 21:30:00 | 154 |
| 2019-01-02 21:45:00 | 156 |
| 2019-01-02 22:00:00 | 154 |
| 2019-01-02 22:15:00 | 152 |
| 2019-01-02 22:30:00 | 96 |
| 2019-01-02 22:45:00 | 95 |
| 2019-01-02 23:00:00 | 96 |
| 2019-01-02 23:15:00 | 101 |
| 2019-01-02 23:30:00 | 100 |
| 2019-01-02 23:45:00 | 93 |
| 2019-01-03 02:30:00 | 142 |
| 2019-01-03 02:45:00 | 138 |
| 2019-01-03 03:00:00 | 142 |
| 2019-01-03 03:15:00 | 142 |
| 2019-01-03 03:30:00 | 146 |
| 2019-01-03 03:45:00 | 153 |
| 2019-01-03 04:00:00 | 153 |
| 2019-01-03 04:15:00 | 151 |
| 2019-01-03 04:30:00 | 146 |
| 2019-01-03 04:45:00 | 142 |
| 2019-01-03 05:00:00 | 139 |
| 2019-01-03 05:15:00 | 136 |
| 2019-01-03 05:30:00 | 135 |
| 2019-01-03 05:45:00 | 134 |
| 2019-01-03 20:00:00 | 178 |
| 2019-01-03 20:15:00 | 180 |
| 2019-01-03 20:30:00 | 180 |
| 2019-01-03 20:45:00 | 178 |
| 2019-01-03 21:00:00 | 169 |
| 2019-01-03 21:15:00 | 161 |
| 2019-01-03 21:30:00 | 155 |
| 2019-01-03 21:45:00 | 148 |
| 2019-01-03 22:00:00 | 144 |
| 2019-01-03 22:15:00 | 143 |
| 2019-01-03 22:30:00 | 144 |
| 2019-01-03 22:45:00 | 132 |
| 2019-01-03 23:00:00 | 154 |
| 2019-01-03 23:15:00 | 145 |
| 2019-01-03 23:30:00 | 127 |
| 2019-01-04 00:45:00 | 130 |
| 2019-01-04 01:00:00 | 140 |
| 2019-01-04 01:15:00 | 159 |
| 2019-01-04 01:30:00 | 176 |
| 2019-01-04 01:45:00 | 180 |
| 2019-01-04 02:00:00 | 175 |
| 2019-01-04 02:15:00 | 176 |
| 2019-01-04 02:30:00 | 200 |
| 2019-01-04 02:45:00 | 200 |
| 2019-01-04 03:00:00 | 200 |
| 2019-01-04 03:15:00 | 202 |
| 2019-01-04 03:30:00 | 181 |
| 2019-01-04 03:45:00 | 178 |
| 2019-01-04 04:00:00 | 176 |
| 2019-01-04 04:15:00 | 175 |
| 2019-01-04 04:30:00 | 175 |
| 2019-01-04 04:45:00 | 172 |
| 2019-01-04 05:00:00 | 170 |
| 2019-01-04 05:15:00 | 169 |
| 2019-01-04 05:30:00 | 165 |
| 2019-01-04 05:45:00 | 163 |
| 2019-01-04 20:00:00 | 174 |
| 2019-01-04 20:15:00 | 173 |
| 2019-01-04 20:30:00 | 175 |
| 2019-01-04 20:45:00 | 181 |
| 2019-01-04 21:00:00 | 192 |
| 2019-01-04 21:15:00 | 199 |
| 2019-01-04 21:30:00 | 203 |
| 2019-01-04 21:45:00 | 210 |
| 2019-01-04 22:00:00 | 229 |
| 2019-01-04 22:15:00 | 229 |
| 2019-01-04 22:30:00 | 226 |
| 2019-01-04 22:45:00 | 228 |
| 2019-01-04 23:00:00 | 241 |
| 2019-01-04 23:15:00 | 231 |
| 2019-01-04 23:30:00 | 205 |
| 2019-01-04 23:45:00 | 210 |
| 2019-01-05 00:00:00 | 216 |
| 2019-01-05 00:15:00 | 219 |
| 2019-01-05 00:30:00 | 223 |
| 2019-01-05 00:45:00 | 225 |
| 2019-01-05 01:00:00 | 190 |
| 2019-01-05 01:15:00 | 185 |
| 2019-01-05 01:30:00 | 185 |
| 2019-01-05 01:45:00 | 185 |
| 2019-01-05 02:00:00 | 179 |
| 2019-01-05 02:15:00 | 169 |
| 2019-01-05 02:30:00 | 161 |
| 2019-01-05 02:45:00 | 152 |
| 2019-01-05 03:00:00 | 144 |
| 2019-01-05 03:15:00 | 142 |
| 2019-01-05 03:30:00 | 143 |
| 2019-01-05 03:45:00 | 146 |
| 2019-01-05 04:00:00 | 140 |
| 2019-01-05 04:15:00 | 169 |
| 2019-01-05 04:30:00 | 164 |
| 2019-01-05 04:45:00 | 161 |
| 2019-01-05 05:00:00 | 160 |
| 2019-01-05 05:15:00 | 156 |
| 2019-01-05 05:30:00 | 152 |
| 2019-01-05 05:45:00 | 148 |
| 2019-01-05 20:00:00 | 192 |
| 2019-01-05 20:15:00 | 191 |
| 2019-01-05 20:30:00 | 195 |
| 2019-01-05 20:45:00 | 199 |
| 2019-01-05 21:00:00 | 199 |
| 2019-01-05 21:15:00 | 203 |
| 2019-01-05 21:30:00 | 215 |
| 2019-01-05 21:45:00 | 181 |
| 2019-01-05 22:00:00 | 202 |
| 2019-01-05 22:15:00 | 213 |
| 2019-01-05 22:30:00 | 211 |
| 2019-01-05 22:45:00 | 224 |
| 2019-01-05 23:00:00 | 222 |
| 2019-01-05 23:15:00 | 193 |
| 2019-01-05 23:30:00 | 190 |
| 2019-01-05 23:45:00 | 189 |
| 2019-01-06 00:00:00 | 193 |
| 2019-01-06 00:15:00 | 198 |
| 2019-01-06 00:30:00 | 193 |
| 2019-01-06 00:45:00 | 189 |
| 2019-01-06 01:00:00 | 192 |
| 2019-01-06 01:15:00 | 192 |
| 2019-01-06 01:30:00 | 206 |
| 2019-01-06 01:45:00 | 198 |
| 2019-01-06 02:00:00 | 186 |
| 2019-01-06 02:15:00 | 180 |
| 2019-01-06 02:30:00 | 177 |
| 2019-01-06 02:45:00 | 172 |
| 2019-01-06 03:00:00 | 166 |
| 2019-01-06 03:15:00 | 157 |
| 2019-01-06 03:30:00 | 151 |
| 2019-01-06 03:45:00 | 149 |
| 2019-01-06 04:00:00 | 148 |
| 2019-01-06 04:15:00 | 149 |
| 2019-01-06 04:30:00 | 149 |
| 2019-01-06 04:45:00 | 150 |
| 2019-01-06 05:00:00 | 150 |
| 2019-01-06 05:15:00 | 152 |
| 2019-01-06 05:30:00 | 154 |
| 2019-01-06 05:45:00 | 154 |
| 2019-01-06 20:00:00 | 247 |
| 2019-01-06 20:15:00 | 238 |
| 2019-01-06 20:30:00 | 228 |
| 2019-01-06 20:45:00 | 216 |
| 2019-01-06 21:00:00 | 197 |
| 2019-01-06 21:15:00 | 184 |
| 2019-01-06 21:30:00 | 160 |
| 2019-01-06 21:45:00 | 159 |
| 2019-01-06 22:00:00 | 154 |
| 2019-01-06 22:15:00 | 142 |
| 2019-01-06 22:30:00 | 123 |
| 2019-01-06 22:45:00 | 110 |
| 2019-01-06 23:00:00 | 106 |
| 2019-01-06 23:15:00 | 105 |
| 2019-01-06 23:30:00 | 93 |
| 2019-01-06 23:45:00 | 86 |
| 2019-01-07 00:00:00 | 80 |
| 2019-01-07 00:15:00 | 80 |
| 2019-01-07 00:30:00 | 83 |
| 2019-01-07 00:45:00 | 80 |
| 2019-01-07 01:00:00 | 74 |
| 2019-01-07 01:15:00 | 72 |
| 2019-01-07 01:30:00 | 73 |
| 2019-01-07 01:45:00 | 79 |
| 2019-01-07 02:00:00 | 118 |
| 2019-01-07 02:15:00 | 119 |
| 2019-01-07 02:30:00 | 120 |
| 2019-01-07 02:45:00 | 122 |
| 2019-01-07 03:00:00 | 133 |
| 2019-01-07 03:15:00 | 133 |
| 2019-01-07 03:30:00 | 134 |
| 2019-01-07 03:45:00 | 138 |
| 2019-01-07 04:00:00 | 146 |
| 2019-01-07 04:15:00 | 151 |
| 2019-01-07 04:30:00 | 150 |
| 2019-01-07 04:45:00 | 150 |
| 2019-01-07 05:00:00 | 148 |
| 2019-01-07 05:15:00 | 144 |
| 2019-01-07 05:30:00 | 142 |
| 2019-01-07 05:45:00 | 143 |
| 2019-01-07 20:00:00 | 180 |
| 2019-01-07 20:15:00 | 180 |
| 2019-01-07 20:30:00 | 175 |
| 2019-01-07 20:45:00 | 167 |
| 2019-01-07 21:00:00 | 158 |
| 2019-01-07 21:15:00 | 150 |
| 2019-01-07 21:30:00 | 145 |
| 2019-01-07 21:45:00 | 145 |
| 2019-01-07 22:00:00 | 146 |
| 2019-01-07 22:15:00 | 154 |
| 2019-01-07 22:30:00 | 147 |
| 2019-01-07 22:45:00 | 140 |
| 2019-01-07 23:00:00 | 136 |
| 2019-01-07 23:15:00 | 132 |
| 2019-01-07 23:30:00 | 129 |
| 2019-01-07 23:45:00 | 127 |
| 2019-01-08 00:00:00 | 124 |
| 2019-01-08 00:15:00 | 122 |
| 2019-01-08 00:30:00 | 120 |
| 2019-01-08 00:45:00 | 121 |
| 2019-01-08 01:00:00 | 121 |
| 2019-01-08 01:15:00 | 125 |
| 2019-01-08 01:30:00 | 134 |
| 2019-01-08 01:45:00 | 138 |
| 2019-01-08 02:00:00 | 143 |
| 2019-01-08 02:15:00 | 146 |
| 2019-01-08 02:30:00 | 147 |
| 2019-01-08 02:45:00 | 151 |
| 2019-01-08 03:00:00 | 206 |
| 2019-01-08 03:15:00 | 203 |
| 2019-01-08 03:30:00 | 201 |
| 2019-01-08 03:45:00 | 198 |
| 2019-01-08 04:00:00 | 179 |
| 2019-01-08 04:15:00 | 175 |
| 2019-01-08 04:30:00 | 171 |
| 2019-01-08 04:45:00 | 170 |
| 2019-01-08 05:00:00 | 171 |
| 2019-01-08 05:15:00 | 173 |
| 2019-01-08 05:30:00 | 173 |
| 2019-01-08 05:45:00 | 168 |
| 2019-01-08 20:15:00 | 141 |
| 2019-01-08 20:30:00 | 132 |
| 2019-01-08 20:45:00 | 124 |
| 2019-01-08 21:00:00 | 120 |
| 2019-01-08 21:15:00 | 123 |
| 2019-01-08 21:30:00 | 124 |
| 2019-01-08 21:45:00 | 132 |
| 2019-01-08 22:00:00 | 137 |
| 2019-01-08 22:15:00 | 145 |
| 2019-01-08 22:30:00 | 146 |
| 2019-01-08 22:45:00 | 148 |
| 2019-01-08 23:00:00 | 148 |
| 2019-01-08 23:15:00 | 138 |
| 2019-01-08 23:30:00 | 139 |
| 2019-01-08 23:45:00 | 156 |
| 2019-01-09 00:00:00 | 159 |
| 2019-01-09 00:15:00 | 160 |
| 2019-01-09 00:30:00 | 160 |
| 2019-01-09 00:45:00 | 158 |
| 2019-01-09 01:00:00 | 157 |
| 2019-01-09 01:15:00 | 162 |
| 2019-01-09 01:30:00 | 171 |
| 2019-01-09 01:45:00 | 181 |
| 2019-01-09 02:00:00 | 182 |
| 2019-01-09 02:15:00 | 163 |
| 2019-01-09 02:30:00 | 155 |
| 2019-01-09 02:45:00 | 149 |
| 2019-01-09 03:00:00 | 147 |
| 2019-01-09 03:15:00 | 148 |
| 2019-01-09 03:30:00 | 145 |
| 2019-01-09 03:45:00 | 145 |
| 2019-01-09 04:00:00 | 148 |
| 2019-01-09 04:45:00 | 159 |
| 2019-01-09 05:15:00 | 156 |
| 2019-01-09 05:30:00 | 157 |
| 2019-01-09 20:00:00 | 209 |
| 2019-01-09 20:15:00 | 207 |
| 2019-01-09 20:30:00 | 207 |
| 2019-01-09 20:45:00 | 203 |
| 2019-01-09 21:00:00 | 195 |
| 2019-01-09 21:15:00 | 183 |
| 2019-01-09 21:30:00 | 164 |
| 2019-01-09 21:45:00 | 148 |
| 2019-01-09 22:00:00 | 137 |
| 2019-01-09 22:15:00 | 127 |
| 2019-01-09 22:30:00 | 116 |
| 2019-01-09 22:45:00 | 112 |
| 2019-01-09 23:00:00 | 118 |
| 2019-01-09 23:15:00 | 141 |
| 2019-01-09 23:30:00 | 144 |
| 2019-01-09 23:45:00 | 164 |
| 2019-01-10 00:00:00 | 167 |
| 2019-01-10 00:15:00 | 152 |
| 2019-01-10 02:30:00 | 161 |
| 2019-01-10 03:00:00 | 168 |
| 2019-01-10 03:15:00 | 166 |
| 2019-01-10 03:30:00 | 161 |
| 2019-01-10 03:45:00 | 155 |
| 2019-01-10 23:00:00 | 153 |
| 2019-01-10 23:15:00 | 168 |
| 2019-01-10 23:30:00 | 178 |
| 2019-01-10 23:45:00 | 178 |
| 2019-01-11 00:00:00 | 179 |
| 2019-01-11 00:15:00 | 178 |
| 2019-01-11 00:30:00 | 171 |
| 2019-01-11 00:45:00 | 168 |
| 2019-01-11 01:00:00 | 162 |
| 2019-01-11 01:15:00 | 153 |
| 2019-01-11 01:30:00 | 147 |
| 2019-01-11 01:45:00 | 143 |
| 2019-01-11 02:00:00 | 142 |
| 2019-01-11 02:15:00 | 140 |
| 2019-01-11 02:30:00 | 138 |
| 2019-01-11 02:45:00 | 136 |
| 2019-01-11 03:00:00 | 134 |
| 2019-01-11 03:15:00 | 132 |
| 2019-01-11 03:30:00 | 131 |
| 2019-01-11 03:45:00 | 130 |
| 2019-01-11 04:00:00 | 129 |
| 2019-01-11 04:15:00 | 132 |
| 2019-01-11 04:30:00 | 131 |
| 2019-01-11 04:45:00 | 130 |
| 2019-01-11 05:00:00 | 128 |
| 2019-01-11 05:15:00 | 126 |
| 2019-01-11 05:30:00 | 126 |
| 2019-01-11 05:45:00 | 123 |
| 2019-01-11 20:15:00 | 140 |
| 2019-01-11 20:30:00 | 156 |
| 2019-01-11 20:45:00 | 164 |
| 2019-01-11 21:00:00 | 179 |
| 2019-01-11 21:15:00 | 185 |
| 2019-01-11 22:15:00 | 298 |
| 2019-01-11 22:30:00 | 308 |
| 2019-01-11 22:45:00 | 312 |
| 2019-01-11 23:00:00 | 315 |
| 2019-01-11 23:15:00 | 331 |
| 2019-01-11 23:30:00 | 339 |
| 2019-01-11 23:45:00 | 325 |
| 2019-01-12 00:00:00 | 310 |
| 2019-01-12 00:15:00 | 284 |
| 2019-01-12 00:30:00 | 181 |
| 2019-01-12 00:45:00 | 162 |
| 2019-01-12 01:00:00 | 143 |
| 2019-01-12 01:15:00 | 122 |
| 2019-01-12 23:00:00 | 120 |
| 2019-01-12 23:15:00 | 122 |
| 2019-01-12 23:30:00 | 124 |
| 2019-01-12 23:45:00 | 107 |
| 2019-01-13 00:00:00 | 118 |
| 2019-01-13 00:15:00 | 123 |
| 2019-01-13 00:30:00 | 127 |
| 2019-01-13 00:45:00 | 124 |
| 2019-01-13 01:00:00 | 126 |
| 2019-01-13 01:15:00 | 139 |
| 2019-01-13 20:00:00 | 145 |
| 2019-01-13 20:15:00 | 141 |
| 2019-01-13 20:30:00 | 135 |
| 2019-01-13 20:45:00 | 128 |
| 2019-01-13 21:00:00 | 122 |
| 2019-01-13 21:15:00 | 119 |
| 2019-01-13 21:30:00 | 111 |
| 2019-01-13 21:45:00 | 95 |
| 2019-01-13 22:00:00 | 94 |
| 2019-01-13 22:15:00 | 106 |
| 2019-01-13 22:30:00 | 138 |
| 2019-01-13 22:45:00 | 164 |
| 2019-01-13 23:00:00 | 190 |
| 2019-01-13 23:15:00 | 171 |
| 2019-01-13 23:30:00 | 179 |
| 2019-01-15 20:00:00 | 102 |
| 2019-01-15 20:15:00 | 104 |
| 2019-01-15 20:30:00 | 109 |
| 2019-01-15 20:45:00 | 115 |
| 2019-01-15 21:00:00 | 117 |
| 2019-01-15 21:15:00 | 114 |
| 2019-01-15 21:30:00 | 107 |
| 2019-01-15 21:45:00 | 101 |
| 2019-01-15 22:00:00 | 101 |
| 2019-01-15 22:15:00 | 80 |
| 2019-01-15 22:30:00 | 96 |
| 2019-01-15 22:45:00 | 115 |
| 2019-01-15 23:00:00 | 120 |
| 2019-01-16 20:00:00 | 164 |
| 2019-01-16 20:15:00 | 160 |
| 2019-01-16 20:30:00 | 146 |
| 2019-01-16 20:45:00 | 133 |
| 2019-01-16 21:00:00 | 129 |
| 2019-01-16 21:15:00 | 124 |
| 2019-01-16 21:30:00 | 119 |
| 2019-01-16 21:45:00 | 116 |
| 2019-01-16 22:00:00 | 117 |
| 2019-01-16 22:15:00 | 122 |
| 2019-01-16 22:30:00 | 120 |
| 2019-01-16 22:45:00 | 127 |
| 2019-01-16 23:00:00 | 137 |
| 2019-01-16 23:15:00 | 133 |
| 2019-01-16 23:30:00 | 127 |
| 2019-01-16 23:45:00 | 126 |
| 2019-01-17 00:00:00 | 130 |
| 2019-01-17 00:15:00 | 134 |
| 2019-01-17 00:45:00 | 142 |
| 2019-01-17 01:00:00 | 144 |
| 2019-01-17 01:30:00 | 146 |
| 2019-01-17 01:45:00 | 153 |
| 2019-01-17 20:15:00 | 145 |
| 2019-01-17 20:30:00 | 140 |
| 2019-01-17 20:45:00 | 140 |
| 2019-01-17 21:00:00 | 153 |
| 2019-01-17 21:15:00 | 148 |
| 2019-01-17 21:30:00 | 164 |
| 2019-01-17 21:45:00 | 195 |
| 2019-01-17 22:00:00 | 192 |
| 2019-01-17 22:15:00 | 178 |
| 2019-01-17 22:30:00 | 167 |
| 2019-01-19 01:30:00 | 147 |
| 2019-01-19 01:45:00 | 140 |
| 2019-01-19 02:00:00 | 133 |
| 2019-01-19 02:15:00 | 131 |
| 2019-01-19 02:30:00 | 129 |
| 2019-01-19 02:45:00 | 127 |
| 2019-01-19 03:00:00 | 126 |
| 2019-01-19 03:15:00 | 121 |
| 2019-01-19 03:30:00 | 117 |
| 2019-01-19 03:45:00 | 116 |
| 2019-01-19 04:00:00 | 140 |
| 2019-01-19 04:15:00 | 138 |
| 2019-01-19 04:30:00 | 132 |
| 2019-01-19 04:45:00 | 124 |
| 2019-01-19 05:00:00 | 120 |
| 2019-01-19 05:15:00 | 115 |
| 2019-01-19 05:30:00 | 114 |
| 2019-01-19 05:45:00 | 112 |
| 2019-01-19 22:00:00 | 177 |
| 2019-01-20 05:45:00 | 178 |
| 2019-01-20 20:15:00 | 218 |
| 2019-01-20 20:30:00 | 212 |
| 2019-01-20 20:45:00 | 206 |
| 2019-01-20 21:00:00 | 204 |
| 2019-01-20 21:15:00 | 199 |
| 2019-01-20 21:30:00 | 227 |
| 2019-01-20 21:45:00 | 165 |
| 2019-01-20 22:00:00 | 156 |
| 2019-01-20 22:15:00 | 152 |
| 2019-01-20 22:30:00 | 148 |
| 2019-01-20 22:45:00 | 141 |
| 2019-01-20 23:00:00 | 140 |
| 2019-01-20 23:15:00 | 158 |
| 2019-01-20 23:30:00 | 161 |
| 2019-01-20 23:45:00 | 159 |
| 2019-01-21 20:00:00 | 166 |
| 2019-01-21 20:15:00 | 158 |
| 2019-01-21 20:30:00 | 159 |
| 2019-01-21 20:45:00 | 167 |
| 2019-01-21 21:00:00 | 175 |
| 2019-01-21 21:15:00 | 184 |
| 2019-01-21 21:30:00 | 196 |
| 2019-01-21 21:45:00 | 200 |
| 2019-01-21 22:00:00 | 175 |
| 2019-01-21 22:15:00 | 172 |
| 2019-01-21 22:30:00 | 158 |
| 2019-01-21 22:45:00 | 142 |
| 2019-01-21 23:00:00 | 126 |
| 2019-01-22 22:15:00 | 198 |
| 2019-01-22 22:30:00 | 195 |
| 2019-01-22 22:45:00 | 196 |
| 2019-01-22 23:00:00 | 209 |
| 2019-01-22 23:15:00 | 156 |
| 2019-01-22 23:30:00 | 157 |
| 2019-01-22 23:45:00 | 152 |
| 2019-01-23 00:00:00 | 144 |
| 2019-01-23 00:15:00 | 135 |
| 2019-01-23 00:30:00 | 131 |
| 2019-01-23 00:45:00 | 134 |
| 2019-01-23 01:00:00 | 153 |
| 2019-01-23 01:15:00 | 156 |
| 2019-01-23 01:30:00 | 155 |
| 2019-01-23 01:45:00 | 154 |
| 2019-01-23 02:00:00 | 159 |
| 2019-01-23 02:15:00 | 165 |
| 2019-01-23 02:30:00 | 168 |
| 2019-01-23 02:45:00 | 165 |
| 2019-01-23 03:00:00 | 159 |
| 2019-01-23 03:15:00 | 153 |
| 2019-01-23 03:30:00 | 147 |
| 2019-01-23 03:45:00 | 140 |
| 2019-01-23 04:00:00 | 134 |
| 2019-01-23 04:15:00 | 130 |
| 2019-01-23 04:30:00 | 128 |
| 2019-01-23 04:45:00 | 127 |
| 2019-01-23 05:00:00 | 124 |
| 2019-01-23 05:15:00 | 118 |
| 2019-01-23 05:30:00 | 114 |
| 2019-01-23 05:45:00 | 113 |
| 2019-01-23 21:45:00 | 222 |
| 2019-01-23 22:15:00 | 198 |
| 2019-01-24 21:45:00 | 111 |
| 2019-01-24 22:00:00 | 115 |
| 2019-01-24 22:15:00 | 103 |
| 2019-01-25 20:00:00 | 152 |
| 2019-01-25 20:15:00 | 155 |
| 2019-01-25 20:30:00 | 168 |
| 2019-01-25 20:45:00 | 175 |
| 2019-01-25 21:00:00 | 174 |
| 2019-01-25 21:15:00 | 173 |
| 2019-01-25 21:30:00 | 164 |
| 2019-01-25 21:45:00 | 198 |
| 2019-01-25 22:00:00 | 216 |
| 2019-01-25 22:15:00 | 280 |
| 2019-01-25 22:30:00 | 279 |
| 2019-01-25 22:45:00 | 277 |
| 2019-01-25 23:00:00 | 279 |
| 2019-01-25 23:15:00 | 279 |
| 2019-01-25 23:30:00 | 276 |
| 2019-01-25 23:45:00 | 272 |
| 2019-01-26 00:00:00 | 271 |
| 2019-01-26 00:15:00 | 215 |
| 2019-01-26 00:30:00 | 212 |
| 2019-01-26 00:45:00 | 202 |
| 2019-01-26 01:00:00 | 195 |
| 2019-01-26 22:15:00 | 192 |
| 2019-01-26 22:30:00 | 204 |
| 2019-01-26 22:45:00 | 255 |
| 2019-01-26 23:00:00 | 241 |
| 2019-01-26 23:15:00 | 215 |
| 2019-01-26 23:30:00 | 191 |
| 2019-01-26 23:45:00 | 176 |
| 2019-01-27 00:00:00 | 156 |
| 2019-01-27 00:15:00 | 154 |
| 2019-01-27 00:30:00 | 147 |
| 2019-01-27 00:45:00 | 138 |
| 2019-01-27 01:00:00 | 134 |
| 2019-01-27 01:15:00 | 190 |
| 2019-01-27 01:30:00 | 185 |
| 2019-01-27 01:45:00 | 184 |
| 2019-01-27 02:00:00 | 175 |
| 2019-01-27 02:15:00 | 169 |
| 2019-01-27 02:30:00 | 168 |
| 2019-01-27 02:45:00 | 170 |
| 2019-01-27 03:00:00 | 164 |
| 2019-01-27 03:15:00 | 155 |
| 2019-01-27 03:30:00 | 152 |
| 2019-01-27 03:45:00 | 151 |
| 2019-01-27 04:00:00 | 148 |
| 2019-01-27 04:15:00 | 145 |
| 2019-01-27 04:30:00 | 148 |
| 2019-01-27 04:45:00 | 152 |
| 2019-01-27 05:00:00 | 154 |
| 2019-01-27 05:15:00 | 156 |
| 2019-01-27 05:30:00 | 157 |
| 2019-01-27 05:45:00 | 157 |
| 2019-01-27 22:45:00 | 251 |
| 2019-01-27 23:00:00 | 219 |
| 2019-01-27 23:15:00 | 207 |
| 2019-01-27 23:30:00 | 194 |
| 2019-01-27 23:45:00 | 176 |
| 2019-01-28 00:00:00 | 162 |
| 2019-01-28 00:15:00 | 150 |
| 2019-01-28 02:00:00 | 103 |
| 2019-01-28 02:15:00 | 101 |
| 2019-01-28 02:30:00 | 98 |
| 2019-01-28 02:45:00 | 96 |
| 2019-01-28 03:00:00 | 93 |
| 2019-01-28 03:15:00 | 88 |
| 2019-01-28 03:30:00 | 84 |
| 2019-01-28 03:45:00 | 82 |
| 2019-01-28 04:00:00 | 84 |
| 2019-01-28 04:15:00 | 86 |
| 2019-01-28 04:30:00 | 85 |
| 2019-01-28 04:45:00 | 83 |
| 2019-01-28 05:00:00 | 84 |
| 2019-01-28 05:15:00 | 83 |
| 2019-01-28 05:30:00 | 82 |
| 2019-01-28 05:45:00 | 80 |
| 2019-01-28 20:00:00 | 166 |
| 2019-01-28 20:15:00 | 166 |
| 2019-01-28 20:30:00 | 167 |
| 2019-01-28 20:45:00 | 184 |
| 2019-01-28 21:00:00 | 193 |
| 2019-01-28 21:15:00 | 193 |
| 2019-01-28 21:30:00 | 194 |
| 2019-01-28 21:45:00 | 226 |
| 2019-01-28 22:00:00 | 196 |
| 2019-01-28 22:15:00 | 193 |
| 2019-01-28 22:30:00 | 184 |
| 2019-01-28 22:45:00 | 176 |
| 2019-01-28 23:00:00 | 180 |
| 2019-01-28 23:15:00 | 208 |
| 2019-01-28 23:30:00 | 215 |
| 2019-01-28 23:45:00 | 188 |
| 2019-01-29 00:00:00 | 187 |
| 2019-01-29 00:15:00 | 189 |
| 2019-01-29 00:30:00 | 191 |
| 2019-01-29 00:45:00 | 190 |
| 2019-01-29 01:00:00 | 187 |
| 2019-01-29 01:15:00 | 184 |
| 2019-01-29 01:30:00 | 181 |
| 2019-01-29 01:45:00 | 169 |
| 2019-01-29 02:00:00 | 165 |
| 2019-01-29 02:15:00 | 162 |
| 2019-01-29 02:30:00 | 169 |
| 2019-01-29 02:45:00 | 178 |
| 2019-01-29 03:00:00 | 175 |
| 2019-01-29 03:15:00 | 170 |
| 2019-01-29 03:30:00 | 168 |
| 2019-01-29 03:45:00 | 166 |
| 2019-01-29 04:00:00 | 163 |
| 2019-01-29 04:15:00 | 159 |
| 2019-01-29 04:30:00 | 156 |
| 2019-01-29 04:45:00 | 156 |
| 2019-01-29 05:00:00 | 155 |
| 2019-01-29 05:30:00 | 143 |
| 2019-01-30 21:30:00 | 139 |
| 2019-01-30 21:45:00 | 140 |
| 2019-01-30 22:00:00 | 144 |
| 2019-01-30 22:15:00 | 138 |
| 2019-01-30 22:30:00 | 137 |
| 2019-01-30 22:45:00 | 155 |
| 2019-01-30 23:00:00 | 159 |
| 2019-01-30 23:15:00 | 178 |
| 2019-01-30 23:30:00 | 177 |
| 2019-01-30 23:45:00 | 174 |
| 2019-01-31 00:00:00 | 178 |
| 2019-01-31 00:15:00 | 185 |
| 2019-01-31 00:30:00 | 181 |
| 2019-01-31 00:45:00 | 178 |
| 2019-01-31 01:00:00 | 181 |
| 2019-01-31 01:15:00 | 184 |
| 2019-01-31 01:30:00 | 186 |
| 2019-01-31 01:45:00 | 185 |
| 2019-01-31 02:00:00 | 178 |
| 2019-01-31 02:15:00 | 166 |
| 2019-01-31 02:30:00 | 166 |
| 2019-01-31 02:45:00 | 160 |
| 2019-01-31 03:00:00 | 150 |
| 2019-01-31 03:15:00 | 161 |
| 2019-01-31 03:30:00 | 162 |
| 2019-01-31 03:45:00 | 156 |
| 2019-01-31 04:00:00 | 150 |
| 2019-01-31 04:15:00 | 146 |
| 2019-01-31 04:30:00 | 142 |
| 2019-01-31 04:45:00 | 136 |
| 2019-01-31 05:00:00 | 131 |
| 2019-01-31 05:15:00 | 128 |
| 2019-01-31 05:30:00 | 126 |
| 2019-01-31 05:45:00 | 124 |
| 2019-01-31 21:00:00 | 165 |
| 2019-01-31 21:15:00 | 168 |
| 2019-01-31 21:30:00 | 168 |
| 2019-01-31 21:45:00 | 172 |
| 2019-01-31 22:00:00 | 176 |
| 2019-01-31 22:15:00 | 167 |
| 2019-01-31 22:30:00 | 154 |
| 2019-01-31 22:45:00 | 146 |
| 2019-01-31 23:00:00 | 145 |
| 2019-01-31 23:15:00 | 144 |
| 2019-01-31 23:30:00 | 141 |
| 2019-01-31 23:45:00 | 145 |
import pandas as pd
# Configurar la opción display.max_columns y display.max_colwidth para mostrar todas las columnas y el texto completo en las celdas
pd.set_option('display.max_columns', None)
pd.set_option('display.max_colwidth', None)
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]
# Filtrar solo las mediciones que están en rango (entre 70 y 180)
in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement'] >= 70) & (df_paciente_22_1_2019['Measurement'] <= 180)]
# Agrupar los datos por día y extraer las horas en las que se estuvo en rango
daily_hours_in_range = in_range.groupby(in_range['Measurement_date'].dt.date)['Measurement_time'].apply(lambda x: x.dt.hour.unique())
# Agrupar los datos por día
daily_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)
# Calcular el porcentaje de tiempo en rango para cada día
daily_percentage_in_range = daily_data.apply(lambda x: (x['Measurement'].between(70, 180)).mean() * 100)
# Crear un nuevo DataFrame con los valores de las horas en rango y el porcentaje de tiempo en rango para cada día
df_resultados = pd.DataFrame({'Día': daily_data.groups.keys(),
'Horas en rango': [f'[{", ".join(map(str, daily_hours_in_range.get(day, [])))}]' for day in daily_data.groups.keys()],
'% tiempo en rango': [f'{daily_percentage_in_range.get(day, 0):.2f}' for day in daily_data.groups.keys()]})
# Mostrar el resultado con los valores de la columna '% tiempo en rango' centrados
df_resultados.style.set_properties(subset=['% tiempo en rango'], **{'text-align': 'center'})
| Día | Horas en rango | % tiempo en rango | |
|---|---|---|---|
| 0 | 2019-01-01 | [12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] | 73.33 |
| 1 | 2019-01-02 | [0, 1, 2, 3, 7, 8, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] | 74.67 |
| 2 | 2019-01-03 | [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] | 100.00 |
| 3 | 2019-01-04 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 20] | 66.67 |
| 4 | 2019-01-05 | [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19] | 72.92 |
| 5 | 2019-01-06 | [2, 3, 4, 5, 6, 7, 9, 10, 11, 12, 14, 21, 22, 23] | 53.95 |
| 6 | 2019-01-07 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 20, 21, 22, 23] | 90.24 |
| 7 | 2019-01-08 | [0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 22, 23] | 87.80 |
| 8 | 2019-01-09 | [0, 1, 2, 3, 4, 5, 6, 11, 14, 16, 17, 18, 21, 22, 23] | 45.00 |
| 9 | 2019-01-10 | [0, 2, 3, 11, 12, 13, 14, 23] | 91.67 |
| 10 | 2019-01-11 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 18, 20, 21] | 83.87 |
| 11 | 2019-01-12 | [0, 1, 9, 10, 11, 12, 13, 14, 15, 17, 18, 23] | 72.00 |
| 12 | 2019-01-13 | [0, 1, 8, 9, 10, 11, 12, 13, 14, 18, 19, 20, 21, 22, 23] | 67.21 |
| 13 | 2019-01-15 | [6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 22, 23] | 100.00 |
| 14 | 2019-01-16 | [11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23] | 96.00 |
| 15 | 2019-01-17 | [0, 1, 20, 21, 22] | 87.50 |
| 16 | 2019-01-19 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 22] | 100.00 |
| 17 | 2019-01-20 | [5, 6, 8, 9, 10, 11, 12, 13, 14, 16, 17, 21, 22, 23] | 63.64 |
| 18 | 2019-01-21 | [19, 20, 21, 22, 23] | 47.83 |
| 19 | 2019-01-22 | [23] | 42.86 |
| 20 | 2019-01-23 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17] | 88.73 |
| 21 | 2019-01-24 | [21, 22] | 100.00 |
| 22 | 2019-01-25 | [11, 12, 15, 16, 17, 18, 19, 20, 21] | 55.77 |
| 23 | 2019-01-26 | [23] | 8.33 |
| 24 | 2019-01-27 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 23] | 75.38 |
| 25 | 2019-01-28 | [0, 2, 3, 4, 5, 6, 7, 14, 15, 16, 17, 18, 19, 20, 22, 23] | 81.97 |
| 26 | 2019-01-29 | [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] | 80.56 |
| 27 | 2019-01-30 | [21, 22, 23] | 100.00 |
| 28 | 2019-01-31 | [0, 2, 3, 4, 5, 6, 7, 10, 11, 12, 13, 14, 15, 19, 21, 22, 23] | 83.33 |
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Calcular el porcentaje de tiempo en rango para la mañana (entre las 6:00 am y las 12:00 pm)
morning_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]
morning_percentage_in_range = (morning_in_range['Measurement'].between(70, 180)).mean() * 100
# Calcular el porcentaje de tiempo en rango para la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]
afternoon_percentage_in_range = (afternoon_in_range['Measurement'].between(70, 180)).mean() * 100
# Calcular el porcentaje de tiempo en rango para la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]
night_percentage_in_range = (night_in_range['Measurement'].between(70, 180)).mean() * 100
# Crear un nuevo DataFrame con los resultados
df_resultados = pd.DataFrame({'Período del día': ['Mañana', 'Tarde', 'Noche'],
'% tiempo en rango': [f'{morning_percentage_in_range:.2f}%',
f'{afternoon_percentage_in_range:.2f}%',
f'{night_percentage_in_range:.2f}%']})
# Mostrar el resultado
display(df_resultados)
| Período del día | % tiempo en rango | |
|---|---|---|
| 0 | Mañana | 88.17% |
| 1 | Tarde | 71.01% |
| 2 | Noche | 74.86% |
import matplotlib.pyplot as plt
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]
# Calcular el porcentaje de tiempo en rango para la mañana (entre las 6:00 am y las 12:00 pm)
morning_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]
morning_percentage_in_range = (morning_in_range['Measurement'].between(70, 180)).mean() * 100
# Calcular el porcentaje de tiempo en rango para la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]
afternoon_percentage_in_range = (afternoon_in_range['Measurement'].between(70, 180)).mean() * 100
# Calcular el porcentaje de tiempo en rango para la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_in_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]
night_percentage_in_range = (night_in_range['Measurement'].between(70, 180)).mean() * 100
# Crear un nuevo DataFrame con los porcentajes de tiempo en rango para la mañana, tarde y noche como columnas
result = pd.DataFrame({'Mañana': [morning_percentage_in_range], 'Tarde': [afternoon_percentage_in_range], 'Noche': [night_percentage_in_range]})
# Crear un gráfico de barras mostrando los porcentajes de tiempo en rango para la mañana, tarde y noche
ax = result.plot(kind='bar', rot=0, color=['#0e4d82', '#bb3f11', '#7d6c5c'])
# Establecer las etiquetas de los ejes X e Y
ax.set_xlabel('Hora del día')
ax.set_ylabel('% Tiempo en rango')
# Añadir título al gráfico
plt.title('Porcentaje de tiempo en rango por hora del día')
# Establecer el rango del eje Y
plt.ylim([0, 100])
# Mostrar los valores exactos de los porcentajes dentro de las barras
for p in ax.patches:
ax.annotate(f'{p.get_height():.2f}%', (p.get_x() + p.get_width() / 2., p.get_height() / 2), ha='center', va='center', color='white', fontweight='bold')
plt.show()
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df[(df['Patient_ID'] == 22) & (df['Measurement_date'].dt.year == 2019) & (df['Measurement_date'].dt.month == 1)]
# Agrupar los datos por hora
hourly_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_time'].dt.hour)
# Calcular la media de la columna Measurement para cada hora
hourly_mean = hourly_data['Measurement'].mean()
# Crear un nuevo DataFrame con los resultados
df_resultados = pd.DataFrame({'Hora': hourly_mean.index, 'Media': hourly_mean.values})
# Formatear la columna Media para mostrar solo dos decimales
df_resultados['Media'] = df_resultados['Media'].apply(lambda x: '{:.2f}'.format(x))
# Mostrar el resultado en forma de tabla de DataFrame sin índice
df_resultados.style.hide(axis='index')
| Hora | Media |
|---|---|
| 0 | 165.51 |
| 1 | 160.29 |
| 2 | 156.67 |
| 3 | 154.38 |
| 4 | 148.72 |
| 5 | 143.32 |
| 6 | 139.92 |
| 7 | 144.65 |
| 8 | 151.18 |
| 9 | 147.76 |
| 10 | 151.13 |
| 11 | 156.65 |
| 12 | 167.71 |
| 13 | 159.31 |
| 14 | 148.80 |
| 15 | 169.62 |
| 16 | 169.23 |
| 17 | 158.60 |
| 18 | 162.03 |
| 19 | 170.74 |
| 20 | 165.62 |
| 21 | 161.85 |
| 22 | 165.24 |
| 23 | 169.26 |
También extrae el porcentaje de tiempo en rango de cada hora
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Agrupar los datos por hora
hourly_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_time'].dt.hour)
# Calcular el porcentaje de tiempo en rango para cada hora
hourly_percentage_in_range = hourly_data.apply(lambda x: (x['Measurement'].between(70, 180)).mean() * 100)
# Encontrar la hora o las horas con el mayor porcentaje de tiempo en rango
max_percentage = hourly_percentage_in_range.max()
best_hours = hourly_percentage_in_range[hourly_percentage_in_range == max_percentage].index
best_hours_str = ', '.join(map(str, best_hours.tolist()))
# Crear un DataFrame con la hora o las horas con el mayor porcentaje de tiempo en rango
df_best_hours = pd.DataFrame({'Hora': [best_hours_str], 'Porcentaje de tiempo en rango': [f'{max_percentage:.2f}%']})
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La hora con el mayor porcentaje de tiempo en rango es:')
display(df_best_hours.style.hide(axis='index'))
top_3 = hourly_percentage_in_range.nlargest(3)
# Crear un DataFrame con las 3 mayores porcentajes de tiempo en rango
df_top_3 = pd.DataFrame({'Hora': top_3.index, 'Porcentaje de tiempo en rango': top_3.apply(lambda x: f'{x:.2f}%')})
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nLas 3 mayores porcentajes de tiempo en rango son:')
display(df_top_3.style.hide(axis='index'))
# Crear un DataFrame con todos los porcentajes de tiempo en rango
df_all_percentages = pd.DataFrame({'Hora': hourly_percentage_in_range.index, 'Porcentaje de tiempo en rango': hourly_percentage_in_range.apply(lambda x: f'{x:.2f}%')})
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nTodos los porcentajes de tiempo en rango son:')
display(df_all_percentages.style.hide(axis='index'))
La hora con el mayor porcentaje de tiempo en rango es:
| Hora | Porcentaje de tiempo en rango |
|---|---|
| 4 | 93.10% |
Las 3 mayores porcentajes de tiempo en rango son:
| Hora | Porcentaje de tiempo en rango |
|---|---|
| 4 | 93.10% |
| 5 | 92.98% |
| 9 | 91.38% |
Todos los porcentajes de tiempo en rango son:
| Hora | Porcentaje de tiempo en rango |
|---|---|
| 0 | 62.69% |
| 1 | 61.29% |
| 2 | 87.30% |
| 3 | 80.88% |
| 4 | 93.10% |
| 5 | 92.98% |
| 6 | 88.71% |
| 7 | 88.71% |
| 8 | 89.09% |
| 9 | 91.38% |
| 10 | 88.52% |
| 11 | 82.46% |
| 12 | 66.67% |
| 13 | 80.00% |
| 14 | 84.06% |
| 15 | 70.69% |
| 16 | 66.67% |
| 17 | 65.08% |
| 18 | 68.12% |
| 19 | 64.52% |
| 20 | 75.00% |
| 21 | 66.67% |
| 22 | 69.32% |
| 23 | 68.97% |
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Filtrar solo las mediciones que están fuera de rango (menor a 70 o mayor a 180)
out_of_range = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement'] < 70) | (df_paciente_22_1_2019['Measurement'] > 180)]
# Agrupar los datos por día y extraer las horas en las que se estuvo fuera de rango
daily_hours_out_of_range = out_of_range.groupby(out_of_range['Measurement_date'].dt.date)['Measurement_time'].apply(lambda x: x.dt.hour.unique())
# Agrupar los datos por día
daily_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_date'].dt.date)
# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para cada día
daily_percentage_hypo = daily_data.apply(lambda x: (x['Measurement'] < 70).mean() * 100)
daily_percentage_hyper = daily_data.apply(lambda x: (x['Measurement'] > 180).mean() * 100)
# Crear un DataFrame con los resultados
data = []
for day in daily_data.groups.keys():
hours = daily_hours_out_of_range.get(day, [])
hours_str = f'[{", ".join(map(str, hours))}]'
percentage_hypo = daily_percentage_hypo.get(day, 0)
percentage_hyper = daily_percentage_hyper.get(day, 0)
data.append({'Día': str(day), 'Horas fuera de rango': hours_str, '% tiempo en hipoglucemia': f'{percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{percentage_hyper:.2f}%'})
df_resultados = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
display(df_resultados.style.hide(axis='index'))
| Día | Horas fuera de rango | % tiempo en hipoglucemia | % tiempo en hiperglucemia |
|---|---|---|---|
| 2019-01-01 | [0, 1, 2, 3] | 0.00% | 26.67% |
| 2019-01-02 | [3, 4, 5, 6, 17, 18] | 0.00% | 25.33% |
| 2019-01-03 | [] | 0.00% | 0.00% |
| 2019-01-04 | [2, 3, 16, 17, 18, 19, 20, 21, 22, 23] | 0.00% | 33.33% |
| 2019-01-05 | [0, 1, 19, 20, 21, 22, 23] | 0.00% | 27.08% |
| 2019-01-06 | [0, 1, 2, 15, 16, 17, 18, 19, 20, 21] | 0.00% | 46.05% |
| 2019-01-07 | [10, 11, 18, 19] | 0.00% | 9.76% |
| 2019-01-08 | [3, 12, 15, 16] | 0.00% | 12.20% |
| 2019-01-09 | [1, 2, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 20, 21] | 0.00% | 55.00% |
| 2019-01-10 | [12, 14] | 0.00% | 8.33% |
| 2019-01-11 | [11, 21, 22, 23] | 0.00% | 16.13% |
| 2019-01-12 | [0, 12, 16, 17, 19] | 0.00% | 28.00% |
| 2019-01-13 | [10, 14, 15, 16, 17, 18, 23] | 0.00% | 32.79% |
| 2019-01-15 | [] | 0.00% | 0.00% |
| 2019-01-16 | [13] | 0.00% | 4.00% |
| 2019-01-17 | [21, 22] | 0.00% | 12.50% |
| 2019-01-19 | [] | 0.00% | 0.00% |
| 2019-01-20 | [6, 7, 11, 17, 18, 19, 20, 21] | 0.00% | 36.36% |
| 2019-01-21 | [17, 18, 19, 21] | 0.00% | 52.17% |
| 2019-01-22 | [22, 23] | 0.00% | 57.14% |
| 2019-01-23 | [11, 12, 13, 21, 22] | 0.00% | 11.27% |
| 2019-01-24 | [] | 0.00% | 0.00% |
| 2019-01-25 | [12, 13, 14, 15, 21, 22, 23] | 0.00% | 44.23% |
| 2019-01-26 | [0, 1, 22, 23] | 0.00% | 91.67% |
| 2019-01-27 | [1, 11, 12, 13, 22, 23] | 0.00% | 24.62% |
| 2019-01-28 | [20, 21, 22, 23] | 0.00% | 18.03% |
| 2019-01-29 | [0, 1] | 0.00% | 19.44% |
| 2019-01-30 | [] | 0.00% | 0.00% |
| 2019-01-31 | [0, 1, 8, 9, 16] | 0.00% | 16.67% |
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para la mañana (entre las 6:00 am y las 12:00 pm)
morning_data = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 6) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 12)]
morning_percentage_hypo = (morning_data['Measurement'] < 70).mean() * 100
morning_percentage_hyper = (morning_data['Measurement'] > 180).mean() * 100
# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para la tarde (entre las 12:00 pm y las 8:00 pm)
afternoon_data = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 12) & (df_paciente_22_1_2019['Measurement_time'].dt.hour < 20)]
afternoon_percentage_hypo = (afternoon_data['Measurement'] < 70).mean() * 100
afternoon_percentage_hyper = (afternoon_data['Measurement'] > 180).mean() * 100
# Calcular el porcentaje de tiempo en hipoglucemia (<70) y hiperglucemia (>180) para la noche (entre las 8:00 pm y las 6:00 am del día siguiente)
night_data = df_paciente_22_1_2019[(df_paciente_22_1_2019['Measurement_time'].dt.hour >= 20) | (df_paciente_22_1_2019['Measurement_time'].dt.hour < 6)]
night_percentage_hypo = (night_data['Measurement'] < 70).mean() * 100
night_percentage_hyper = (night_data['Measurement'] > 180).mean() * 100
# Crear un DataFrame con los resultados
data = [{'Parte del día': 'Mañana', '% tiempo en hipoglucemia': f'{morning_percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{morning_percentage_hyper:.2f}%'},
{'Parte del día': 'Tarde', '% tiempo en hipoglucemia': f'{afternoon_percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{afternoon_percentage_hyper:.2f}%'},
{'Parte del día': 'Noche', '% tiempo en hipoglucemia': f'{night_percentage_hypo:.2f}%', '% tiempo en hiperglucemia': f'{night_percentage_hyper:.2f}%'}]
df_resultados = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
display(df_resultados.style.hide(axis='index'))
| Parte del día | % tiempo en hipoglucemia | % tiempo en hiperglucemia |
|---|---|---|
| Mañana | 0.00% | 11.83% |
| Tarde | 0.00% | 28.99% |
| Noche | 0.00% | 25.14% |
También extrae el porcentaje de tiempo en rango de cada hora
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_paciente_22_1_2019 = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Agrupar los datos por hora
hourly_data = df_paciente_22_1_2019.groupby(df_paciente_22_1_2019['Measurement_time'].dt.hour)
# Calcular el porcentaje de tiempo en hipoglucemia (<70) e hiperglucemia (>180) para cada hora
hourly_percentage_hypo = hourly_data.apply(lambda x: (x['Measurement'] < 70).mean() * 100)
hourly_percentage_hyper = hourly_data.apply(lambda x: (x['Measurement'] > 180).mean() * 100)
# Encontrar la hora o las horas con el mayor porcentaje de tiempo en hipoglucemia
max_percentage_hypo = hourly_percentage_hypo.max()
best_hours_hypo = hourly_percentage_hypo[hourly_percentage_hypo == max_percentage_hypo].index
best_hours_str_hypo = ', '.join(map(str, best_hours_hypo.tolist()))
# Encontrar la hora o las horas con el mayor porcentaje de tiempo en hiperglucemia
max_percentage_hyper = hourly_percentage_hyper.max()
best_hours_hyper = hourly_percentage_hyper[hourly_percentage_hyper == max_percentage_hyper].index
best_hours_str_hyper = ', '.join(map(str, best_hours_hyper.tolist()))
# Crear un DataFrame con la hora o las horas con el mayor porcentaje de tiempo en hipoglucemia e hiperglucemia
data = [{'Hora': best_hours_str_hypo, 'Porcentaje de tiempo': f'{max_percentage_hypo:.2f}%', 'Tipo': 'Hipoglucemia'},
{'Hora': best_hours_str_hyper, 'Porcentaje de tiempo': f'{max_percentage_hyper:.2f}%', 'Tipo': 'Hiperglucemia'}]
df_best_hours = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La hora con el mayor porcentaje de tiempo en hipoglucemia e hiperglucemia es:')
display(df_best_hours.style.hide(axis='index'))
# Encontrar las 3 horas con los mayores porcentajes de tiempo en hipoglucemia
top_3_hypo = hourly_percentage_hypo.nlargest(3)
# Crear un DataFrame con las 3 horas con los mayores porcentajes de tiempo en hipoglucemia
data = [{'Hora': hour, 'Porcentaje de tiempo': f'{percentage:.2f}%'} for hour, percentage in top_3_hypo.items()]
df_top_3_hypo = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nLas 3 mayores porcentajes de tiempo en hipoglucemia son:')
display(df_top_3_hypo.style.hide(axis='index'))
# Encontrar las 3 horas con los mayores porcentajes de tiempo en hiperglucemia
top_3_hyper = hourly_percentage_hyper.nlargest(3)
# Crear un DataFrame con las 3 horas con los mayores porcentajes de tiempo en hiperglucemia
data = [{'Hora': hour, 'Porcentaje de tiempo': f'{percentage:.2f}%'} for hour, percentage in top_3_hyper.items()]
df_top_3_hyper = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nLas 3 mayores porcentajes de tiempo en hiperglucemia son:')
display(df_top_3_hyper.style.hide(axis='index'))
# Crear un DataFrame con todos los porcentajes de tiempo en hipoglucemia e hiperglucemia para cada hora
data = [{'Hora': hour, '% tiempo en hipoglucemia': f'{hourly_percentage_hypo.get(hour, 0):.2f}%', '% tiempo en hiperglucemia': f'{hourly_percentage_hyper.get(hour, 0):.2f}%'} for hour in range(24)]
df_all_percentages = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('\nTodos los porcentajes de tiempo en hipoglucemia e hiperglucemia para cada hora son:')
display(df_all_percentages.style.hide(axis='index'))
La hora con el mayor porcentaje de tiempo en hipoglucemia e hiperglucemia es:
| Hora | Porcentaje de tiempo | Tipo |
|---|---|---|
| 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 | 0.00% | Hipoglucemia |
| 1 | 38.71% | Hiperglucemia |
Las 3 mayores porcentajes de tiempo en hipoglucemia son:
| Hora | Porcentaje de tiempo |
|---|---|
| 0 | 0.00% |
| 1 | 0.00% |
| 2 | 0.00% |
Las 3 mayores porcentajes de tiempo en hiperglucemia son:
| Hora | Porcentaje de tiempo |
|---|---|
| 1 | 38.71% |
| 0 | 37.31% |
| 19 | 35.48% |
Todos los porcentajes de tiempo en hipoglucemia e hiperglucemia para cada hora son:
| Hora | % tiempo en hipoglucemia | % tiempo en hiperglucemia |
|---|---|---|
| 0 | 0.00% | 37.31% |
| 1 | 0.00% | 38.71% |
| 2 | 0.00% | 12.70% |
| 3 | 0.00% | 19.12% |
| 4 | 0.00% | 6.90% |
| 5 | 0.00% | 7.02% |
| 6 | 0.00% | 11.29% |
| 7 | 0.00% | 11.29% |
| 8 | 0.00% | 10.91% |
| 9 | 0.00% | 8.62% |
| 10 | 0.00% | 11.48% |
| 11 | 0.00% | 17.54% |
| 12 | 0.00% | 33.33% |
| 13 | 0.00% | 20.00% |
| 14 | 0.00% | 15.94% |
| 15 | 0.00% | 29.31% |
| 16 | 0.00% | 33.33% |
| 17 | 0.00% | 34.92% |
| 18 | 0.00% | 31.88% |
| 19 | 0.00% | 35.48% |
| 20 | 0.00% | 25.00% |
| 21 | 0.00% | 33.33% |
| 22 | 0.00% | 30.68% |
| 23 | 0.00% | 31.03% |
df_paciente_22_2019 = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]
meses = df_paciente_22_2019['Measurement_date'].dt.month.unique()
# Crear un DataFrame con los meses para los que hay datos
df_meses = pd.DataFrame({'Meses': meses})
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('Hay datos para los siguientes meses del año 2019 para el paciente 22:')
display(df_meses.style.hide(axis='index'))
if len(meses) == 12:
print('Hay datos para todos los meses del año 2019 para el paciente 22')
else:
print('Faltan datos para algunos meses del año 2019 para el paciente 22')
Hay datos para los siguientes meses del año 2019 para el paciente 22:
| Meses |
|---|
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
Faltan datos para algunos meses del año 2019 para el paciente 22
# Crear una lista de todos los meses en un año
todos_meses = list(range(1,13))
df_paciente_22_2019 = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]
meses_con_datos = df_paciente_22_2019['Measurement_date'].dt.month.unique()
# Encontrar los meses que no están en la lista de meses con datos
meses_sin_datos = [mes for mes in todos_meses if mes not in meses_con_datos]
# Crear un DataFrame con los meses para los que hay datos y los que no
df_meses_con_datos = pd.DataFrame({'Meses con datos': meses_con_datos})
df_meses_sin_datos = pd.DataFrame({'Meses sin datos': meses_sin_datos})
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('Para el paciente 22 en el año 2019:')
display(df_meses_con_datos.style.hide(axis='index'))
display(df_meses_sin_datos.style.hide(axis='index'))
if len(meses_con_datos) == 12:
print('Hay datos para todos los meses.')
else:
print('Faltan datos para algunos meses.')
Para el paciente 22 en el año 2019:
| Meses con datos |
|---|
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 8 |
| 9 |
| 10 |
| 11 |
| 12 |
| Meses sin datos |
|---|
| 7 |
Faltan datos para algunos meses.
df_filtered = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]
# Crear un DataFrame con la media de la hemoglobina glicosilada HbA1c para cada mes
data = []
for month in df_filtered['Measurement_date'].dt.month.unique():
df_month = df_filtered[df_filtered['Measurement_date'].dt.month == month]
eAG = df_month['Measurement'].mean()
A1c = (eAG + 46.7) / 28.7
data.append({'Mes': month, 'A1c promedio': f'{A1c:.2f}%'})
df_resultados = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La media de la hemoglobina glicosilada HbA1c de todos los meses del año 2019 del paciente 22 es:')
display(df_resultados.style.hide(axis='index'))
La media de la hemoglobina glicosilada HbA1c de todos los meses del año 2019 del paciente 22 es:
| Mes | A1c promedio |
|---|---|
| 1 | 7.14% |
| 2 | 7.13% |
| 3 | 7.27% |
| 4 | 7.54% |
| 5 | 7.01% |
| 6 | 7.29% |
| 8 | 7.19% |
| 9 | 7.24% |
| 10 | 6.89% |
| 11 | 6.89% |
| 12 | 7.29% |
import matplotlib.pyplot as plt
# Filtrar los datos para el paciente 22 en el año 2019
df_filtered = df_paciente_22_2019[(df_paciente_22_2019['Patient_ID'] == 22) & (df_paciente_22_2019['Measurement_date'].dt.year == 2019)]
# Calcular la media de la hemoglobina glicosilada HbA1c para cada mes
months = []
a1c_values = []
for month in df_filtered['Measurement_date'].dt.month.unique():
df_month = df_filtered[df_filtered['Measurement_date'].dt.month == month]
eAG = df_month['Measurement'].mean()
A1c = (eAG + 46.7) / 28.7
months.append(month)
a1c_values.append(A1c)
# Crear un nuevo DataFrame con los meses y los valores de HbA1c como columnas
result = pd.DataFrame({'Month': months, 'HbA1c': a1c_values})
# Crear un gráfico de barras o de líneas mostrando la media de la hemoglobina glicosilada HbA1c para cada mes
ax = result.plot.bar(x='Month', y='HbA1c', rot=0)
# ax = result.plot(x='Month', y='HbA1c')
# Establecer las etiquetas de los ejes X e Y
ax.set_xlabel('Month')
ax.set_ylabel('HbA1c (%)')
plt.show()
df_filtered = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Crear un DataFrame con la hemoglobina glicosilada HbA1c para cada día
data = []
for day in df_filtered['Measurement_date'].dt.day.unique():
df_day = df_filtered[df_filtered['Measurement_date'].dt.day == day]
eAG = df_day['Measurement'].mean()
A1c = (eAG + 46.7) / 28.7
data.append({'Día': day, 'A1c': f'{A1c:.2f}%'})
df_resultados = pd.DataFrame(data)
# Mostrar el resultado en forma de tabla de DataFrame sin índice
print('La hemoglobina glicosilada HbA1c de todos los días del mes 1, del año 2019, del paciente 22 es:')
display(df_resultados.style.hide(axis='index'))
La hemoglobina glicosilada HbA1c de todos los días del mes 1, del año 2019, del paciente 22 es:
| Día | A1c |
|---|---|
| 1 | 7.08% |
| 2 | 7.00% |
| 3 | 6.80% |
| 4 | 7.53% |
| 5 | 7.34% |
| 6 | 7.87% |
| 7 | 6.76% |
| 8 | 6.96% |
| 9 | 7.79% |
| 10 | 7.41% |
| 11 | 7.52% |
| 12 | 7.04% |
| 13 | 7.39% |
| 15 | 5.96% |
| 16 | 6.47% |
| 17 | 7.01% |
| 19 | 5.97% |
| 20 | 7.68% |
| 21 | 7.79% |
| 22 | 7.91% |
| 23 | 6.91% |
| 24 | 5.45% |
| 25 | 7.73% |
| 26 | 9.09% |
| 27 | 7.46% |
| 28 | 6.29% |
| 29 | 7.05% |
| 30 | 7.00% |
| 31 | 7.03% |
from matplotlib.figure import Figure
from IPython.display import display
# Filtrar los datos para el paciente 22 en el mes 1 del año 2019
df_filtered = df_paciente_22_1_2019[(df_paciente_22_1_2019['Patient_ID'] == 22) & (df_paciente_22_1_2019['Measurement_date'].dt.year == 2019) & (df_paciente_22_1_2019['Measurement_date'].dt.month == 1)]
# Calcular la hemoglobina glicosilada HbA1c para cada día
days = []
a1c_values = []
for day in df_filtered['Measurement_date'].dt.day.unique():
df_day = df_filtered[df_filtered['Measurement_date'].dt.day == day]
eAG = df_day['Measurement'].mean()
A1c = (eAG + 46.7) / 28.7
days.append(day)
a1c_values.append(A1c)
# Crear un nuevo DataFrame con los días y los valores de HbA1c como columnas
result = pd.DataFrame({'Day': days, 'HbA1c': a1c_values})
# Crear una instancia de la clase Figure y establecer su tamaño
fig = Figure()
fig.set_size_inches(5, 5)
# Crear un objeto Axes a partir de la figura
ax = fig.subplots()
# Utilizar el objeto Axes para crear el gráfico de barras
ax.bar(result['Day'], result['HbA1c'])
ax.set_xlabel('Day')
ax.set_ylabel('HbA1c (%)')
# Mostrar la figura
display(fig)
El Clarke Error Grid permite determinar la calidad y precisión del modelo ya que evalúa y cuantifica el riesgo asociado con las estimaciones de glucosa en sangre en comparación con los valores precisos conocidos. Ayuda a determinar qué tan cerca o lejos están las estimaciones de los valores reales y clasificar las estimaciones en diferentes zonas de riesgo. Las zonas de riesgo son A, B, C, D y E:
En resumen, las zonas A y B son las más deseables, indicando resultados precisos o aceptables. La zona C implica un tratamiento innecesario, mientras que la zona D representa la incapacidad para detectar una situación peligrosa. La zona E es la más crítica, indicando la confusión entre hipoglucemia grave e hiperglucemia.
df
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-02-21 19:45:00 | 83 | 2018-02-21 | 1900-01-01 19:46:00 | 121 | True | False | False | 19 | 2 |
| 2018-02-21 20:00:00 | 83 | 2018-02-21 | 1900-01-01 20:01:00 | 124 | True | False | False | 20 | 2 |
| 2018-02-21 20:15:00 | 83 | 2018-02-21 | 1900-01-01 20:17:00 | 131 | True | False | False | 20 | 2 |
| 2018-02-21 20:30:00 | 83 | 2018-02-21 | 1900-01-01 20:32:00 | 128 | True | False | False | 20 | 2 |
| 2018-02-21 20:45:00 | 83 | 2018-02-21 | 1900-01-01 20:47:00 | 129 | True | False | False | 20 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-21 19:45:00 | 27 | 2022-03-21 | 1900-01-01 19:48:00 | 264 | False | False | True | 19 | 0 |
| 2022-03-21 20:00:00 | 27 | 2022-03-21 | 1900-01-01 20:03:00 | 269 | False | False | True | 20 | 0 |
| 2022-03-21 20:15:00 | 27 | 2022-03-21 | 1900-01-01 20:18:00 | 283 | False | False | True | 20 | 0 |
| 2022-03-21 20:30:00 | 27 | 2022-03-21 | 1900-01-01 20:33:00 | 315 | False | False | True | 20 | 0 |
| 2022-03-21 20:45:00 | 27 | 2022-03-21 | 1900-01-01 20:48:00 | 327 | False | False | True | 20 | 0 |
136340 rows × 9 columns
df.dtypes
Patient_ID Int32 Measurement_date datetime64[ns] Measurement_time datetime64[ns] Measurement Int64 In_Range bool Hypoglycemia bool Hyperglycemia bool Hour_of_Day Int64 Day_of_Week Int64 dtype: object
df_paciente_22
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-11-06 06:45:00 | 22 | 2018-11-06 | 1900-01-01 06:45:00 | 134 | True | False | False | 6 | 1 |
| 2018-11-06 07:00:00 | 22 | 2018-11-06 | 1900-01-01 07:00:00 | 134 | True | False | False | 7 | 1 |
| 2018-11-06 07:15:00 | 22 | 2018-11-06 | 1900-01-01 07:15:00 | 139 | True | False | False | 7 | 1 |
| 2018-11-06 07:30:00 | 22 | 2018-11-06 | 1900-01-01 07:30:00 | 145 | True | False | False | 7 | 1 |
| 2018-11-06 07:45:00 | 22 | 2018-11-06 | 1900-01-01 07:45:00 | 147 | True | False | False | 7 | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-08 11:30:00 | 22 | 2022-03-08 | 1900-01-01 11:30:00 | 179 | True | False | False | 11 | 1 |
| 2022-03-08 12:30:00 | 22 | 2022-03-08 | 1900-01-01 12:30:00 | 173 | True | False | False | 12 | 1 |
| 2022-03-08 12:45:00 | 22 | 2022-03-08 | 1900-01-01 12:45:00 | 171 | True | False | False | 12 | 1 |
| 2022-03-08 13:15:00 | 22 | 2022-03-08 | 1900-01-01 13:15:00 | 165 | True | False | False | 13 | 1 |
| 2022-03-15 23:45:00 | 22 | 2022-03-15 | 1900-01-01 23:46:00 | 155 | True | False | False | 23 | 1 |
10409 rows × 9 columns
df_paciente_22.dtypes
Patient_ID Int32 Measurement_date datetime64[ns] Measurement_time datetime64[ns] Measurement Int64 In_Range bool Hypoglycemia bool Hyperglycemia bool Hour_of_Day Int64 Day_of_Week Int64 dtype: object
Se va a realizar una comparativa de soluciones con los modelos de redes neuronales recurrentes (RNN) y redes neuronales recurrentes convolucionales (CRNN), utilizando series temporales para la predicción de los niveles de glucosa en diabetes tipo 1. El rendimiento de ambos modelos se va a tratar de mejorar mediante la combinación de los distintos parámetros de los algoritmos profundos y de las distintas extracciones de características del conjunto de datos.
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y contar el número de mediciones ('Measurement_time') que cada paciente ha realizado.
# Luego ordenar los resultados de manera descendente ('sort_values(ascending=False)') y seleccionar los primeros 5 pacientes con más mediciones ('head(5)').
top_5_pacientes = df.groupby('Patient_ID')['Measurement_time'].count().sort_values(ascending=False).head(5)
# Crear un nuevo DataFrame 'df_top_5_pacientes_modificado' que contiene los mismos datos que 'top_5_pacientes',
# pero reseteamos el índice para que el ID de paciente y el número de mediciones estén en columnas separadas.
df_top_5_pacientes_modificado = top_5_pacientes.reset_index()
# Renombrar las columnas del DataFrame 'df_top_5_pacientes_modificado'.
df_top_5_pacientes_modificado.columns = ['Patient_ID', 'Num_Mediciones']
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y obtener la fecha mínima ('min') y máxima ('max') de las mediciones.
# Luego resetear el índice y se guarda en el DataFrame 'fechas_min_max'.
fechas_min_max = df.groupby('Patient_ID')['Measurement_date'].agg(['min', 'max']).reset_index()
# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'fechas_min_max' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(fechas_min_max, on='Patient_ID')
# Agrupar el DataFrame 'df' por el ID de paciente ('Patient_ID') y calcular la media de la columna 'Measurement' para cada paciente
media_glucosa = df.groupby('Patient_ID')['Measurement'].mean()
# Crear un nuevo DataFrame 'df_media_glucosa' que contiene los mismos datos que 'media_glucosa',
# pero reseteamos el índice para que el ID de paciente y la media de glucosa estén en columnas separadas.
df_media_glucosa = media_glucosa.reset_index()
# Renombrar las columnas del DataFrame 'df_media_glucosa'.
df_media_glucosa.columns = ['Patient_ID', 'Media_Glucosa']
# Unir el DataFrame 'df_top_5_pacientes_modificado' y el DataFrame 'df_media_glucosa' en una nueva DataFrame 'df_top_5_pacientes_modificado'
# Utilizar el ID de paciente ('Patient_ID') como clave de unión.
df_top_5_pacientes_modificado = df_top_5_pacientes_modificado.merge(df_media_glucosa, on='Patient_ID')
df_top_5_pacientes_modificado
| Patient_ID | Num_Mediciones | min | max | Media_Glucosa | |
|---|---|---|---|---|---|
| 0 | 24 | 11987 | 2018-07-13 | 2022-03-12 | 151.588888 |
| 1 | 11 | 11332 | 2018-06-12 | 2022-02-25 | 150.851747 |
| 2 | 22 | 10409 | 2018-11-06 | 2022-03-15 | 157.267653 |
| 3 | 83 | 9198 | 2018-02-21 | 2022-03-14 | 144.642314 |
| 4 | 92 | 8509 | 2018-06-06 | 2022-01-10 | 152.568692 |
La comparativa extensa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 22 debido a que tras un análisis extenso del paciente 22 se ha detectado que es el que prácticamente tiene más mediciones y es el que tiene un mayor número de mediciones en un mes, lo cual facilita al algoritmo en descubrir patrones en los datos.
Posteriormente a la comparativa de soluciones del paciente 22, se llevará a cabo más comparativas de soluciones con los modelos RNN y CRNN pero con el resto de los 5 pacientes con más mediciones. De esta manera se podrá confirmar que el experimento se puede llevar a cabo de manera personalizada para cada uno de los pacientes.
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo LSTM con la variable 'Measurement'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error
import matplotlib.pyplot as plt
from tensorflow.keras.optimizers import Adam
from tensorflow.keras.layers import BatchNormalization
from tensorflow.keras.layers import Dropout
from tensorflow.keras.callbacks import EarlyStopping
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement']], seq_length)
X_val, y_val = create_sequences(val[['Measurement']], seq_length)
X_test, y_test = create_sequences(test[['Measurement']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 1))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], 1)))
model.add(Dense(1))
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_45"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_55 (LSTM) (None, 32) 4352
dense_45 (Dense) (None, 1) 33
=================================================================
Total params: 4,385
Trainable params: 4,385
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1471/1471 [==============================] - 4s 2ms/step - loss: 136.4953 - val_loss: 124.6187 Epoch 2/20 1471/1471 [==============================] - 3s 2ms/step - loss: 104.0943 - val_loss: 92.3128 Epoch 3/20 1471/1471 [==============================] - 3s 2ms/step - loss: 72.6040 - val_loss: 61.3360 Epoch 4/20 1471/1471 [==============================] - 3s 2ms/step - loss: 42.4538 - val_loss: 30.9649 Epoch 5/20 1471/1471 [==============================] - 3s 2ms/step - loss: 20.2102 - val_loss: 12.0011 Epoch 6/20 1471/1471 [==============================] - 3s 2ms/step - loss: 12.3273 - val_loss: 6.8520 Epoch 7/20 1471/1471 [==============================] - 3s 2ms/step - loss: 8.0267 - val_loss: 3.7079 Epoch 8/20 1471/1471 [==============================] - 3s 2ms/step - loss: 5.8618 - val_loss: 2.2610 Epoch 9/20 1471/1471 [==============================] - 3s 2ms/step - loss: 4.5002 - val_loss: 1.0836 Epoch 10/20 1471/1471 [==============================] - 3s 2ms/step - loss: 3.6757 - val_loss: 1.1721 Epoch 11/20 1471/1471 [==============================] - 3s 2ms/step - loss: 3.2800 - val_loss: 0.9048 Epoch 12/20 1471/1471 [==============================] - 3s 2ms/step - loss: 4.1149 - val_loss: 1.7230 Epoch 13/20 1471/1471 [==============================] - 3s 2ms/step - loss: 4.1932 - val_loss: 1.2855 Epoch 14/20 1471/1471 [==============================] - 3s 2ms/step - loss: 3.6879 - val_loss: 1.4613
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_1 = train_eval
val_eval_measurement_1 = val_eval
test_eval_measurement_1 = test_eval
train_rmse_measurement_1 = train_rmse
val_rmse_measurement_1 = val_rmse
test_rmse_measurement_1 = test_rmse
2942/2942 [==============================] - 2s 613us/step 368/368 [==============================] - 0s 607us/step 368/368 [==============================] - 0s 601us/step Evaluación Entrenamiento MAE: 3.397733211517334 Evaluación Validación MAE: 1.4613102674484253 Evaluación Prueba MAE: 3.6630656719207764 Entrenamiento RMSE: 9.165555953979492 Validación RMSE: 2.6851067543029785 Prueba RMSE: 5.405536651611328
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df['Measurement'].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 1))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], next_prediction)
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 8ms/step
Datos reales Datos predichos
date
2022-03-16 00:01:00 NaN 151.623505
2022-03-16 00:16:00 NaN 151.369003
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement']], seq_length)
X_val, y_val = create_sequences(val[['Measurement']], seq_length)
X_test, y_test = create_sequences(test[['Measurement']], seq_length)
# 13. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 1))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))
# 14. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1))
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_2 = train_eval
val_eval_measurement_2 = val_eval
test_eval_measurement_2 = test_eval
train_rmse_measurement_2 = train_rmse
val_rmse_measurement_2 = val_rmse
test_rmse_measurement_2 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_47"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_57 (LSTM) (None, 64) 16896
dense_47 (Dense) (None, 1) 65
=================================================================
Total params: 16,961
Trainable params: 16,961
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 4s 3ms/step - loss: 3.2362 - val_loss: 2.1548
Epoch 2/20
1471/1471 [==============================] - 4s 3ms/step - loss: 2.3219 - val_loss: 0.9614
Epoch 3/20
1471/1471 [==============================] - 4s 3ms/step - loss: 2.3921 - val_loss: 0.4749
Epoch 4/20
1471/1471 [==============================] - 4s 3ms/step - loss: 2.1076 - val_loss: 1.0579
Epoch 5/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.9189 - val_loss: 0.3827
Epoch 6/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.8168 - val_loss: 0.5475
Epoch 7/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.6238 - val_loss: 0.5080
Epoch 8/20
1471/1471 [==============================] - 4s 3ms/step - loss: 1.5767 - val_loss: 0.9051
2942/2942 [==============================] - 3s 910us/step
368/368 [==============================] - 0s 892us/step
368/368 [==============================] - 0s 970us/step
Evaluación Entrenamiento MAE: 1.6751281023025513
Evaluación Validación MAE: 0.9051337242126465
Evaluación Prueba MAE: 0.755179762840271
Entrenamiento RMSE: 4.604981899261475
Validación RMSE: 1.474887728691101
Prueba RMSE: 1.4225993156433105
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement']], seq_length)
X_val, y_val = create_sequences(val[['Measurement']], seq_length)
X_test, y_test = create_sequences(test[['Measurement']], seq_length)
# 13. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 1))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 1))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 1))
# 14. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1))
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_3 = train_eval
val_eval_measurement_3 = val_eval
test_eval_measurement_3 = test_eval
train_rmse_measurement_3 = train_rmse
val_rmse_measurement_3 = val_rmse
test_rmse_measurement_3 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_2"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_2 (LSTM) (None, 8, 32) 4352
batch_normalization (BatchN (None, 8, 32) 128
ormalization)
dropout (Dropout) (None, 8, 32) 0
lstm_3 (LSTM) (None, 64) 24832
batch_normalization_1 (Batc (None, 64) 256
hNormalization)
dropout_1 (Dropout) (None, 64) 0
dense_2 (Dense) (None, 1) 65
=================================================================
Total params: 29,633
Trainable params: 29,441
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 7s 4ms/step - loss: 45.8393 - val_loss: 4.0092
Epoch 2/20
1471/1471 [==============================] - 5s 4ms/step - loss: 22.3210 - val_loss: 8.9894
Epoch 3/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.7114 - val_loss: 4.3612
Epoch 4/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.5136 - val_loss: 3.8598
Epoch 5/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.5876 - val_loss: 8.5723
Epoch 6/20
1471/1471 [==============================] - 5s 4ms/step - loss: 21.8220 - val_loss: 5.3411
Epoch 7/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.5005 - val_loss: 12.6982
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 12.524332046508789
Evaluación Validación MAE: 12.698190689086914
Evaluación Prueba MAE: 18.400575637817383
Entrenamiento RMSE: 15.80256175994873
Validación RMSE: 13.540170669555664
Prueba RMSE: 19.12810707092285
En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement' y 'In_Range'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | In_Range | |
|---|---|---|
| Datetime | ||
| 2018-11-06 06:45:00 | 134 | True |
| 2018-11-06 07:00:00 | 134 | True |
| 2018-11-06 07:15:00 | 139 | True |
| 2018-11-06 07:30:00 | 145 | True |
| 2018-11-06 07:45:00 | 147 | True |
| ... | ... | ... |
| 2022-03-08 11:30:00 | 179 | True |
| 2022-03-08 12:30:00 | 173 | True |
| 2022-03-08 12:45:00 | 171 | True |
| 2022-03-08 13:15:00 | 165 | True |
| 2022-03-15 23:46:00 | 155 | True |
10409 rows × 2 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','In_Range']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','In_Range']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','In_Range']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_3"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_4 (LSTM) (None, 64) 17152
dense_3 (Dense) (None, 1) 65
=================================================================
Total params: 17,217
Trainable params: 17,217
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1471/1471 [==============================] - 4s 2ms/step - loss: 3.5523 - val_loss: 1.6889 Epoch 2/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.2653 - val_loss: 0.7579 Epoch 3/20 1471/1471 [==============================] - 4s 2ms/step - loss: 2.3494 - val_loss: 2.6885 Epoch 4/20 1471/1471 [==============================] - 4s 2ms/step - loss: 2.1578 - val_loss: 0.8448 Epoch 5/20 1471/1471 [==============================] - 3s 2ms/step - loss: 1.9449 - val_loss: 0.3595 Epoch 6/20 1471/1471 [==============================] - 3s 2ms/step - loss: 1.6986 - val_loss: 0.7306 Epoch 7/20 1471/1471 [==============================] - 3s 2ms/step - loss: 1.6241 - val_loss: 1.4357 Epoch 8/20 1471/1471 [==============================] - 4s 2ms/step - loss: 1.6380 - val_loss: 0.5561
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_in_range_2 = train_eval
val_eval_measurement_in_range_2 = val_eval
test_eval_measurement_in_range_2 = test_eval
train_rmse_measurement_in_range_2 = train_rmse
val_rmse_measurement_in_range_2 = val_rmse
test_rmse_measurement_in_range_2 = test_rmse
2942/2942 [==============================] - 2s 760us/step 368/368 [==============================] - 0s 751us/step 368/368 [==============================] - 0s 746us/step Evaluación Entrenamiento MAE: 1.232211709022522 Evaluación Validación MAE: 0.5560938119888306 Evaluación Prueba MAE: 0.7522381544113159 Entrenamiento RMSE: 4.56373929977417 Validación RMSE: 1.2793986797332764 Prueba RMSE: 1.3832286596298218
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement','In_Range']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 2))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1]]], axis=0)
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-03-16 00:01:00 NaN 157.013489
2022-03-16 00:16:00 NaN 157.832733
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','In_Range']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','In_Range']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','In_Range']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_in_range_1 = train_eval
val_eval_measurement_in_range_1 = val_eval
test_eval_measurement_in_range_1 = test_eval
train_rmse_measurement_in_range_1 = train_rmse
val_rmse_measurement_in_range_1 = val_rmse
test_rmse_measurement_in_range_1 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_4"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_5 (LSTM) (None, 32) 4480
dense_4 (Dense) (None, 1) 33
=================================================================
Total params: 4,513
Trainable params: 4,513
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 3s 2ms/step - loss: 138.2115 - val_loss: 127.9240
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 110.2278 - val_loss: 101.1393
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 81.9239 - val_loss: 71.8314
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 53.8821 - val_loss: 43.9747
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 28.7584 - val_loss: 18.8059
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 15.2615 - val_loss: 7.9287
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 9.4990 - val_loss: 4.8718
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 6.6924 - val_loss: 2.8957
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.9679 - val_loss: 1.8840
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.1110 - val_loss: 1.2146
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.4092 - val_loss: 1.3017
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.0485 - val_loss: 1.3891
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.8782 - val_loss: 0.8578
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6136 - val_loss: 0.6092
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.5512 - val_loss: 0.4022
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2845 - val_loss: 0.9302
Epoch 17/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2159 - val_loss: 0.7827
Epoch 18/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1603 - val_loss: 0.3276
Epoch 19/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1245 - val_loss: 0.6638
Epoch 20/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1497 - val_loss: 0.3016
2942/2942 [==============================] - 2s 632us/step
368/368 [==============================] - 0s 620us/step
368/368 [==============================] - 0s 614us/step
Evaluación Entrenamiento MAE: 1.6543866395950317
Evaluación Validación MAE: 0.3015759587287903
Evaluación Prueba MAE: 0.5337554216384888
Entrenamiento RMSE: 6.209878444671631
Validación RMSE: 1.3075438737869263
Prueba RMSE: 1.662801742553711
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','In_Range']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','In_Range']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','In_Range']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_in_range_3 = train_eval
val_eval_measurement_in_range_3 = val_eval
test_eval_measurement_in_range_3 = test_eval
train_rmse_measurement_in_range_3 = train_rmse
val_rmse_measurement_in_range_3 = val_rmse
test_rmse_measurement_in_range_3 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_5"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_6 (LSTM) (None, 8, 32) 4480
batch_normalization_2 (Batc (None, 8, 32) 128
hNormalization)
dropout_2 (Dropout) (None, 8, 32) 0
lstm_7 (LSTM) (None, 64) 24832
batch_normalization_3 (Batc (None, 64) 256
hNormalization)
dropout_3 (Dropout) (None, 64) 0
dense_5 (Dense) (None, 1) 65
=================================================================
Total params: 29,761
Trainable params: 29,569
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 6s 4ms/step - loss: 46.5253 - val_loss: 4.5927
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.2459 - val_loss: 10.4085
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.4915 - val_loss: 2.7437
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.1844 - val_loss: 4.5216
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.0268 - val_loss: 3.9724
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.0547 - val_loss: 4.5230
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 7.778632640838623
Evaluación Validación MAE: 4.523031711578369
Evaluación Prueba MAE: 9.269298553466797
Entrenamiento RMSE: 10.855286598205566
Validación RMSE: 5.989138126373291
Prueba RMSE: 10.361227989196777
En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-11-06 06:45:00 | 134 | False | False |
| 2018-11-06 07:00:00 | 134 | False | False |
| 2018-11-06 07:15:00 | 139 | False | False |
| 2018-11-06 07:30:00 | 145 | False | False |
| 2018-11-06 07:45:00 | 147 | False | False |
| ... | ... | ... | ... |
| 2022-03-08 11:30:00 | 179 | False | False |
| 2022-03-08 12:30:00 | 173 | False | False |
| 2022-03-08 12:45:00 | 171 | False | False |
| 2022-03-08 13:15:00 | 165 | False | False |
| 2022-03-15 23:46:00 | 155 | False | False |
10409 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_46"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_56 (LSTM) (None, 64) 17408
dense_46 (Dense) (None, 1) 65
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1471/1471 [==============================] - 5s 3ms/step - loss: 3.3848 - val_loss: 1.9171 Epoch 2/20 1471/1471 [==============================] - 4s 3ms/step - loss: 2.0156 - val_loss: 0.9414 Epoch 3/20 1471/1471 [==============================] - 4s 3ms/step - loss: 1.8913 - val_loss: 0.3186 Epoch 4/20 1471/1471 [==============================] - 4s 3ms/step - loss: 1.7138 - val_loss: 0.3235 Epoch 5/20 1471/1471 [==============================] - 4s 3ms/step - loss: 1.8326 - val_loss: 0.1901 Epoch 6/20 1471/1471 [==============================] - 4s 3ms/step - loss: 1.6533 - val_loss: 1.6578 Epoch 7/20 1471/1471 [==============================] - 4s 3ms/step - loss: 1.5178 - val_loss: 0.9782 Epoch 8/20 1471/1471 [==============================] - 4s 3ms/step - loss: 1.4218 - val_loss: 0.2717
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2 = train_eval
val_eval_measurement_hypo_hype_2 = val_eval
test_eval_measurement_hypo_hype_2 = test_eval
train_rmse_measurement_hypo_hype_2 = train_rmse
val_rmse_measurement_hypo_hype_2 = val_rmse
test_rmse_measurement_hypo_hype_2 = test_rmse
2942/2942 [==============================] - 2s 810us/step 368/368 [==============================] - 0s 867us/step 368/368 [==============================] - 0s 852us/step Evaluación Entrenamiento MAE: 1.083967924118042 Evaluación Validación MAE: 0.27166786789894104 Evaluación Prueba MAE: 0.23148754239082336 Entrenamiento RMSE: 4.52763557434082 Validación RMSE: 1.2004188299179077 Prueba RMSE: 1.2222604751586914
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
# Almacena dato
future_predictions_hypo_hype_2_LSTM_22 = future_predictions
plt.show()
1/1 [==============================] - 0s 11ms/step
1/1 [==============================] - 0s 9ms/step
Datos reales Datos predichos
date
2022-03-16 00:01:00 NaN 156.141495
2022-03-16 00:16:00 NaN 157.410568
def clarke_error_grid(ref_values, pred_values):
assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))
if max(ref_values) > 400 or max(pred_values) > 400:
print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
if min(ref_values) < 0 or min(pred_values) < 0:
print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values), min(pred_values)))
plt.clf()
plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
plt.title("Clarke Error Grid")
plt.xlabel("Reference Concentration (mg/dl)")
plt.ylabel("Prediction Concentration (mg/dl)")
plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.gca().set_facecolor('white')
plt.gca().set_xlim([0, 400])
plt.gca().set_ylim([0, 400])
plt.gca().set_aspect((400)/(400))
plt.plot([0, 400], [0, 400], ':', c='black')
plt.plot([0, 175/3], [70, 70], '-', c='black')
plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
plt.plot([70, 70], [84, 400],'-', c='black')
plt.plot([0, 70], [180, 180], '-', c='black')
plt.plot([70, 290], [180, 400],'-', c='black')
plt.plot([70, 70], [0, 56], '-', c='black')
plt.plot([70, 400], [56, 320],'-', c='black')
plt.plot([180, 180], [0, 70], '-', c='black')
plt.plot([180, 400], [70, 70], '-', c='black')
plt.plot([240, 240], [70, 180],'-', c='black')
plt.plot([240, 400], [180, 180], '-', c='black')
plt.plot([130, 180], [0, 70], '-', c='black')
plt.text(30, 15, "A", fontsize=15)
plt.text(370, 260, "B", fontsize=15)
plt.text(280, 370, "B", fontsize=15)
plt.text(160, 370, "C", fontsize=15)
plt.text(160, 15, "C", fontsize=15)
plt.text(30, 140, "D", fontsize=15)
plt.text(370, 120, "D", fontsize=15)
plt.text(30, 370, "E", fontsize=15)
plt.text(370, 15, "E", fontsize=15)
zone = [0] * 5
for i in range(len(ref_values)):
if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
zone[0] += 1 # Zone A
elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
zone[4] += 1 # Zone E
elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
zone[2] += 1 # Zone C
elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
zone[3] += 1 # Zone D
else:
zone[1] += 1 # Zone B
return plt, zone
ref_values = y_test
pred_values = y_pred_test
plt, zone = clarke_error_grid(ref_values, pred_values)
# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_22 = plt.gcf()
# Mostrar la figura
plt.show()
# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})
# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total
# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))
# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_22 = zone_df
# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_22.style.hide(axis="index"))
Conteo de Zonas:
| Zona | Conteo | Proporción |
|---|---|---|
| A | 11758 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1 = train_eval
val_eval_measurement_hypo_hype_1 = val_eval
test_eval_measurement_hypo_hype_1 = test_eval
train_rmse_measurement_hypo_hype_1 = train_rmse
val_rmse_measurement_hypo_hype_1 = val_rmse
test_rmse_measurement_hypo_hype_1 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_7"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_9 (LSTM) (None, 32) 4608
dense_7 (Dense) (None, 1) 33
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 3s 2ms/step - loss: 132.2171 - val_loss: 116.9254
Epoch 2/20
1471/1471 [==============================] - 2s 2ms/step - loss: 93.7074 - val_loss: 79.4056
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 57.6149 - val_loss: 44.0134
Epoch 4/20
1471/1471 [==============================] - 2s 2ms/step - loss: 26.5661 - val_loss: 14.2901
Epoch 5/20
1471/1471 [==============================] - 2s 2ms/step - loss: 13.1676 - val_loss: 7.3467
Epoch 6/20
1471/1471 [==============================] - 2s 2ms/step - loss: 8.9232 - val_loss: 4.5158
Epoch 7/20
1471/1471 [==============================] - 2s 2ms/step - loss: 6.5820 - val_loss: 2.9944
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.8992 - val_loss: 1.8385
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.7555 - val_loss: 1.1614
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.3712 - val_loss: 1.0437
Epoch 11/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.8549 - val_loss: 1.0787
Epoch 12/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.6591 - val_loss: 0.6316
Epoch 13/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.4153 - val_loss: 0.9110
Epoch 14/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.3556 - val_loss: 0.4507
Epoch 15/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.1673 - val_loss: 0.7386
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1262 - val_loss: 0.4601
Epoch 17/20
1471/1471 [==============================] - 2s 2ms/step - loss: 2.0089 - val_loss: 0.6385
2942/2942 [==============================] - 2s 597us/step
368/368 [==============================] - 0s 605us/step
368/368 [==============================] - 0s 604us/step
Evaluación Entrenamiento MAE: 1.9722120761871338
Evaluación Validación MAE: 0.6385322213172913
Evaluación Prueba MAE: 1.0579259395599365
Entrenamiento RMSE: 6.278008937835693
Validación RMSE: 1.4417616128921509
Prueba RMSE: 1.9623427391052246
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3 = train_eval
val_eval_measurement_hypo_hype_3 = val_eval
test_eval_measurement_hypo_hype_3 = test_eval
train_rmse_measurement_hypo_hype_3 = train_rmse
val_rmse_measurement_hypo_hype_3 = val_rmse
test_rmse_measurement_hypo_hype_3 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_8"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_10 (LSTM) (None, 8, 32) 4608
batch_normalization_4 (Batc (None, 8, 32) 128
hNormalization)
dropout_4 (Dropout) (None, 8, 32) 0
lstm_11 (LSTM) (None, 64) 24832
batch_normalization_5 (Batc (None, 64) 256
hNormalization)
dropout_5 (Dropout) (None, 64) 0
dense_8 (Dense) (None, 1) 65
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 6s 4ms/step - loss: 46.2266 - val_loss: 9.3853
Epoch 2/20
1471/1471 [==============================] - 5s 4ms/step - loss: 22.6362 - val_loss: 12.1679
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.9584 - val_loss: 9.6945
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.7004 - val_loss: 4.8474
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.4922 - val_loss: 12.1168
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.3319 - val_loss: 3.7308
Epoch 7/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.2087 - val_loss: 11.0984
Epoch 8/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.1116 - val_loss: 6.3882
Epoch 9/20
1471/1471 [==============================] - 6s 4ms/step - loss: 20.9531 - val_loss: 4.2203
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 7.9226298332214355
Evaluación Validación MAE: 4.220335006713867
Evaluación Prueba MAE: 6.241281509399414
Entrenamiento RMSE: 10.941481590270996
Validación RMSE: 5.068966388702393
Prueba RMSE: 6.4750542640686035
En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement' y 'Hour_of_Day'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hour_of_Day']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hour_of_Day | |
|---|---|---|
| Datetime | ||
| 2018-11-06 06:45:00 | 134 | 6 |
| 2018-11-06 07:00:00 | 134 | 7 |
| 2018-11-06 07:15:00 | 139 | 7 |
| 2018-11-06 07:30:00 | 145 | 7 |
| 2018-11-06 07:45:00 | 147 | 7 |
| ... | ... | ... |
| 2022-03-08 11:30:00 | 179 | 11 |
| 2022-03-08 12:30:00 | 173 | 12 |
| 2022-03-08 12:45:00 | 171 | 12 |
| 2022-03-08 13:15:00 | 165 | 13 |
| 2022-03-15 23:46:00 | 155 | 23 |
10409 rows × 2 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Hour_of_Day']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Hour_of_Day']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Hour_of_Day']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_9"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_12 (LSTM) (None, 64) 17152
dense_9 (Dense) (None, 1) 65
=================================================================
Total params: 17,217
Trainable params: 17,217
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1471/1471 [==============================] - 4s 2ms/step - loss: 5.0938 - val_loss: 1.2173 Epoch 2/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.2982 - val_loss: 0.6090 Epoch 3/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.0070 - val_loss: 0.6723 Epoch 4/20 1471/1471 [==============================] - 3s 2ms/step - loss: 1.8999 - val_loss: 0.3173 Epoch 5/20 1471/1471 [==============================] - 3s 2ms/step - loss: 3.3002 - val_loss: 1.2668 Epoch 6/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.1917 - val_loss: 1.1737 Epoch 7/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.0090 - val_loss: 1.0570
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hour_of_day_2 = train_eval
val_eval_measurement_hour_of_day_2 = val_eval
test_eval_measurement_hour_of_day_2 = test_eval
train_rmse_measurement_hour_of_day_2 = train_rmse
val_rmse_measurement_hour_of_day_2 = val_rmse
test_rmse_measurement_hour_of_day_2 = test_rmse
2942/2942 [==============================] - 2s 746us/step 368/368 [==============================] - 0s 836us/step 368/368 [==============================] - 0s 850us/step Evaluación Entrenamiento MAE: 1.9983710050582886 Evaluación Validación MAE: 1.0570324659347534 Evaluación Prueba MAE: 1.3511601686477661 Entrenamiento RMSE: 5.443111896514893 Validación RMSE: 1.727100133895874 Prueba RMSE: 1.9237534999847412
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement','Hour_of_Day']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 2))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1]]], axis=0)
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-03-16 00:01:00 NaN 166.313065
2022-03-16 00:16:00 NaN 166.716141
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hour_of_Day']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Hour_of_Day']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Hour_of_Day']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Hour_of_Day']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hour_of_day_1 = train_eval
val_eval_measurement_hour_of_day_1 = val_eval
test_eval_measurement_hour_of_day_1 = test_eval
train_rmse_measurement_hour_of_day_1 = train_rmse
val_rmse_measurement_hour_of_day_1 = val_rmse
test_rmse_measurement_hour_of_day_1 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_10"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_13 (LSTM) (None, 32) 4480
dense_10 (Dense) (None, 1) 33
=================================================================
Total params: 4,513
Trainable params: 4,513
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 3s 2ms/step - loss: 134.4768 - val_loss: 121.8707
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 101.3275 - val_loss: 89.0795
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 68.1067 - val_loss: 55.9350
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 36.8675 - val_loss: 24.5940
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 19.2243 - val_loss: 12.2342
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 13.6366 - val_loss: 7.5060
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 9.2897 - val_loss: 4.5421
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 6.5768 - val_loss: 3.8769
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.8175 - val_loss: 1.8758
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.7758 - val_loss: 1.7478
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.3433 - val_loss: 1.1201
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.0773 - val_loss: 0.7236
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.7448 - val_loss: 0.5562
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6129 - val_loss: 1.0078
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.1278 - val_loss: 0.9319
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.7623 - val_loss: 1.4178
2942/2942 [==============================] - 2s 600us/step
368/368 [==============================] - 0s 620us/step
368/368 [==============================] - 0s 649us/step
Evaluación Entrenamiento MAE: 3.570218086242676
Evaluación Validación MAE: 1.4177560806274414
Evaluación Prueba MAE: 1.9302120208740234
Entrenamiento RMSE: 7.898557662963867
Validación RMSE: 2.019634246826172
Prueba RMSE: 3.149078607559204
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hour_of_Day']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Hour_of_Day']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Hour_of_Day']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Hour_of_Day']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hour_of_day_3 = train_eval
val_eval_measurement_hour_of_day_3 = val_eval
test_eval_measurement_hour_of_day_3 = test_eval
train_rmse_measurement_hour_of_day_3 = train_rmse
val_rmse_measurement_hour_of_day_3 = val_rmse
test_rmse_measurement_hour_of_day_3 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_11"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_14 (LSTM) (None, 8, 32) 4480
batch_normalization_6 (Batc (None, 8, 32) 128
hNormalization)
dropout_6 (Dropout) (None, 8, 32) 0
lstm_15 (LSTM) (None, 64) 24832
batch_normalization_7 (Batc (None, 64) 256
hNormalization)
dropout_7 (Dropout) (None, 64) 0
dense_11 (Dense) (None, 1) 65
=================================================================
Total params: 29,761
Trainable params: 29,569
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 7s 4ms/step - loss: 46.1576 - val_loss: 6.3130
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.0568 - val_loss: 2.4649
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.6295 - val_loss: 2.3236
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.2521 - val_loss: 4.1637
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.0167 - val_loss: 4.2129
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 20.8337 - val_loss: 3.9539
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 5.086625099182129
Evaluación Validación MAE: 3.9538815021514893
Evaluación Prueba MAE: 6.964335918426514
Entrenamiento RMSE: 7.240564346313477
Validación RMSE: 4.714972019195557
Prueba RMSE: 7.407690525054932
En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement' y 'Day_of_Week'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Day_of_Week']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Day_of_Week | |
|---|---|---|
| Datetime | ||
| 2018-11-06 06:45:00 | 134 | 1 |
| 2018-11-06 07:00:00 | 134 | 1 |
| 2018-11-06 07:15:00 | 139 | 1 |
| 2018-11-06 07:30:00 | 145 | 1 |
| 2018-11-06 07:45:00 | 147 | 1 |
| ... | ... | ... |
| 2022-03-08 11:30:00 | 179 | 1 |
| 2022-03-08 12:30:00 | 173 | 1 |
| 2022-03-08 12:45:00 | 171 | 1 |
| 2022-03-08 13:15:00 | 165 | 1 |
| 2022-03-15 23:46:00 | 155 | 1 |
10409 rows × 2 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Day_of_Week']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_12"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_16 (LSTM) (None, 64) 17152
dense_12 (Dense) (None, 1) 65
=================================================================
Total params: 17,217
Trainable params: 17,217
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1471/1471 [==============================] - 4s 3ms/step - loss: 3.9463 - val_loss: 0.7817 Epoch 2/20 1471/1471 [==============================] - 4s 3ms/step - loss: 3.2367 - val_loss: 0.5049 Epoch 3/20 1471/1471 [==============================] - 4s 2ms/step - loss: 2.3581 - val_loss: 1.2754 Epoch 4/20 1471/1471 [==============================] - 4s 2ms/step - loss: 2.3379 - val_loss: 0.3002 Epoch 5/20 1471/1471 [==============================] - 4s 2ms/step - loss: 1.9858 - val_loss: 0.5236 Epoch 6/20 1471/1471 [==============================] - 4s 3ms/step - loss: 1.9003 - val_loss: 2.0343 Epoch 7/20 1471/1471 [==============================] - 4s 2ms/step - loss: 1.8998 - val_loss: 0.7044
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_day_of_week_2 = train_eval
val_eval_measurement_day_of_week_2 = val_eval
test_eval_measurement_day_of_week_2 = test_eval
train_rmse_measurement_day_of_week_2 = train_rmse
val_rmse_measurement_day_of_week_2 = val_rmse
test_rmse_measurement_day_of_week_2 = test_rmse
2942/2942 [==============================] - 2s 779us/step 368/368 [==============================] - 0s 797us/step 368/368 [==============================] - 0s 819us/step Evaluación Entrenamiento MAE: 1.6692419052124023 Evaluación Validación MAE: 0.7044217586517334 Evaluación Prueba MAE: 0.7746291756629944 Entrenamiento RMSE: 5.377539157867432 Validación RMSE: 1.4903850555419922 Prueba RMSE: 1.5528700351715088
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement','Day_of_Week']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 2))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1]]], axis=0)
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-03-16 00:01:00 NaN 160.964401
2022-03-16 00:16:00 NaN 159.243057
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Day_of_Week']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Day_of_Week']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_day_of_week_1 = train_eval
val_eval_measurement_day_of_week_1 = val_eval
test_eval_measurement_day_of_week_1 = test_eval
train_rmse_measurement_day_of_week_1 = train_rmse
val_rmse_measurement_day_of_week_1 = val_rmse
test_rmse_measurement_day_of_week_1 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_13"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_17 (LSTM) (None, 32) 4480
dense_13 (Dense) (None, 1) 33
=================================================================
Total params: 4,513
Trainable params: 4,513
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 131.7301 - val_loss: 116.5562
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 94.3545 - val_loss: 80.7936
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 59.1237 - val_loss: 45.6152
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 27.6796 - val_loss: 15.1195
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 14.1706 - val_loss: 7.6964
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 10.1934 - val_loss: 5.4373
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 7.6666 - val_loss: 3.7413
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 5.7240 - val_loss: 2.5617
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.3452 - val_loss: 2.3716
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.4809 - val_loss: 1.7590
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.0224 - val_loss: 0.7280
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6887 - val_loss: 0.6233
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.5725 - val_loss: 0.8415
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.4848 - val_loss: 0.4222
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2404 - val_loss: 0.7913
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1203 - val_loss: 0.4545
Epoch 17/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0784 - val_loss: 0.2821
Epoch 18/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0595 - val_loss: 0.9754
Epoch 19/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0513 - val_loss: 0.8418
Epoch 20/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.0260 - val_loss: 1.7636
2942/2942 [==============================] - 2s 602us/step
368/368 [==============================] - 0s 605us/step
368/368 [==============================] - 0s 615us/step
Evaluación Entrenamiento MAE: 3.097968339920044
Evaluación Validación MAE: 1.7635772228240967
Evaluación Prueba MAE: 1.5313645601272583
Entrenamiento RMSE: 6.313864231109619
Validación RMSE: 2.127568483352661
Prueba RMSE: 2.175009250640869
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Day_of_Week']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement','Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement','Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement','Day_of_Week']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 2))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 2))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 2))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identifica el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_day_of_week_3 = train_eval
val_eval_measurement_day_of_week_3 = val_eval
test_eval_measurement_day_of_week_3 = test_eval
train_rmse_measurement_day_of_week_3 = train_rmse
val_rmse_measurement_day_of_week_3 = val_rmse
test_rmse_measurement_day_of_week_3 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_14"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_18 (LSTM) (None, 8, 32) 4480
batch_normalization_8 (Batc (None, 8, 32) 128
hNormalization)
dropout_8 (Dropout) (None, 8, 32) 0
lstm_19 (LSTM) (None, 64) 24832
batch_normalization_9 (Batc (None, 64) 256
hNormalization)
dropout_9 (Dropout) (None, 64) 0
dense_14 (Dense) (None, 1) 65
=================================================================
Total params: 29,761
Trainable params: 29,569
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 7s 4ms/step - loss: 46.9868 - val_loss: 10.4243
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 23.1711 - val_loss: 5.7973
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.5526 - val_loss: 6.8161
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.0047 - val_loss: 6.2960
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.8285 - val_loss: 7.2971
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 10.655786514282227
Evaluación Validación MAE: 7.297052383422852
Evaluación Prueba MAE: 13.426828384399414
Entrenamiento RMSE: 15.592889785766602
Validación RMSE: 8.962603569030762
Prueba RMSE: 14.679518699645996
En este caso se esta entrenando al algoritmo LSTM con las variables 'Measurement', 'In_Range', 'Hypoglycemia' y 'Hyperglycemia', 'Hour_of_Day' y 'Day_of_Week'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|
| Datetime | ||||||
| 2018-11-06 06:45:00 | 134 | True | False | False | 6 | 1 |
| 2018-11-06 07:00:00 | 134 | True | False | False | 7 | 1 |
| 2018-11-06 07:15:00 | 139 | True | False | False | 7 | 1 |
| 2018-11-06 07:30:00 | 145 | True | False | False | 7 | 1 |
| 2018-11-06 07:45:00 | 147 | True | False | False | 7 | 1 |
| ... | ... | ... | ... | ... | ... | ... |
| 2022-03-08 11:30:00 | 179 | True | False | False | 11 | 1 |
| 2022-03-08 12:30:00 | 173 | True | False | False | 12 | 1 |
| 2022-03-08 12:45:00 | 171 | True | False | False | 12 | 1 |
| 2022-03-08 13:15:00 | 165 | True | False | False | 13 | 1 |
| 2022-03-15 23:46:00 | 155 | True | False | False | 23 | 1 |
10409 rows × 6 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 6))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 6))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 6))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_15"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_20 (LSTM) (None, 64) 18176
dense_15 (Dense) (None, 1) 65
=================================================================
Total params: 18,241
Trainable params: 18,241
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1471/1471 [==============================] - 4s 2ms/step - loss: 4.3364 - val_loss: 0.6861 Epoch 2/20 1471/1471 [==============================] - 3s 2ms/step - loss: 3.2986 - val_loss: 0.4602 Epoch 3/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.7819 - val_loss: 1.0866 Epoch 4/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.6742 - val_loss: 1.1946 Epoch 5/20 1471/1471 [==============================] - 3s 2ms/step - loss: 2.6183 - val_loss: 0.8242
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_combined_2 = train_eval
val_eval_measurement_combined_2 = val_eval
test_eval_measurement_combined_2 = test_eval
train_rmse_measurement_combined_2 = train_rmse
val_rmse_measurement_combined_2 = val_rmse
test_rmse_measurement_combined_2 = test_rmse
2942/2942 [==============================] - 2s 797us/step 368/368 [==============================] - 0s 795us/step 368/368 [==============================] - 0s 784us/step Evaluación Entrenamiento MAE: 2.2896623611450195 Evaluación Validación MAE: 0.8242446780204773 Evaluación Prueba MAE: 0.6581457853317261 Entrenamiento RMSE: 6.420724391937256 Validación RMSE: 1.8279951810836792 Prueba RMSE: 1.839697003364563
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 6))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2], last_sequence[-1][3], last_sequence[-1][4], last_sequence[-1][5]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-03-16 00:01:00 NaN 166.969177
2022-03-16 00:16:00 NaN 172.455338
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 6)) # 6 features
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 6)) # 6 features
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 6)) # 6 features
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_combined_1 = train_eval
val_eval_measurement_combined_1 = val_eval
test_eval_measurement_combined_1 = test_eval
train_rmse_measurement_combined_1 = train_rmse
val_rmse_measurement_combined_1 = val_rmse
test_rmse_measurement_combined_1 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_16"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_21 (LSTM) (None, 32) 4992
dense_16 (Dense) (None, 1) 33
=================================================================
Total params: 5,025
Trainable params: 5,025
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 4s 2ms/step - loss: 137.2627 - val_loss: 126.2613
Epoch 2/20
1471/1471 [==============================] - 3s 2ms/step - loss: 108.4822 - val_loss: 99.3464
Epoch 3/20
1471/1471 [==============================] - 3s 2ms/step - loss: 77.6350 - val_loss: 65.0552
Epoch 4/20
1471/1471 [==============================] - 3s 2ms/step - loss: 45.7832 - val_loss: 34.2348
Epoch 5/20
1471/1471 [==============================] - 3s 2ms/step - loss: 21.7077 - val_loss: 12.2683
Epoch 6/20
1471/1471 [==============================] - 3s 2ms/step - loss: 13.8918 - val_loss: 7.6678
Epoch 7/20
1471/1471 [==============================] - 3s 2ms/step - loss: 10.3435 - val_loss: 5.6352
Epoch 8/20
1471/1471 [==============================] - 3s 2ms/step - loss: 7.9973 - val_loss: 4.3661
Epoch 9/20
1471/1471 [==============================] - 3s 2ms/step - loss: 6.2528 - val_loss: 3.0041
Epoch 10/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.9268 - val_loss: 2.6749
Epoch 11/20
1471/1471 [==============================] - 3s 2ms/step - loss: 4.1266 - val_loss: 1.6302
Epoch 12/20
1471/1471 [==============================] - 3s 2ms/step - loss: 3.2765 - val_loss: 1.0367
Epoch 13/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.9762 - val_loss: 0.9002
Epoch 14/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.8334 - val_loss: 0.8995
Epoch 15/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.6069 - val_loss: 0.8952
Epoch 16/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.5612 - val_loss: 0.6054
Epoch 17/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.4186 - val_loss: 0.6025
Epoch 18/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.3298 - val_loss: 0.8918
Epoch 19/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.1645 - val_loss: 0.7523
Epoch 20/20
1471/1471 [==============================] - 3s 2ms/step - loss: 2.2164 - val_loss: 0.6855
2942/2942 [==============================] - 2s 632us/step
368/368 [==============================] - 0s 606us/step
368/368 [==============================] - 0s 614us/step
Evaluación Entrenamiento MAE: 2.390277147293091
Evaluación Validación MAE: 0.6855114102363586
Evaluación Prueba MAE: 1.2360053062438965
Entrenamiento RMSE: 6.2993340492248535
Validación RMSE: 1.504172921180725
Prueba RMSE: 2.051300048828125
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'In_Range', 'Hypoglycemia', 'Hyperglycemia', 'Hour_of_Day', 'Day_of_Week']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 6)) # 6 features
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 6)) # 6 features
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 6)) # 6 features
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_combined_3 = train_eval
val_eval_measurement_combined_3 = val_eval
test_eval_measurement_combined_3 = test_eval
train_rmse_measurement_combined_3 = train_rmse
val_rmse_measurement_combined_3 = val_rmse
test_rmse_measurement_combined_3 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_17"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_22 (LSTM) (None, 8, 32) 4992
batch_normalization_10 (Bat (None, 8, 32) 128
chNormalization)
dropout_10 (Dropout) (None, 8, 32) 0
lstm_23 (LSTM) (None, 64) 24832
batch_normalization_11 (Bat (None, 64) 256
chNormalization)
dropout_11 (Dropout) (None, 64) 0
dense_17 (Dense) (None, 1) 65
=================================================================
Total params: 30,273
Trainable params: 30,081
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1471/1471 [==============================] - 8s 4ms/step - loss: 46.2283 - val_loss: 9.3527
Epoch 2/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.9136 - val_loss: 15.1247
Epoch 3/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.1880 - val_loss: 28.7162
Epoch 4/20
1471/1471 [==============================] - 6s 4ms/step - loss: 23.0935 - val_loss: 7.8719
Epoch 5/20
1471/1471 [==============================] - 6s 4ms/step - loss: 23.0679 - val_loss: 8.3499
Epoch 6/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.8313 - val_loss: 8.2024
Epoch 7/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.7284 - val_loss: 7.0599
Epoch 8/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.4004 - val_loss: 6.6235
Epoch 9/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.4001 - val_loss: 9.3511
Epoch 10/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.2407 - val_loss: 6.1467
Epoch 11/20
1471/1471 [==============================] - 6s 4ms/step - loss: 22.1233 - val_loss: 7.3190
Epoch 12/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.8646 - val_loss: 4.9952
Epoch 13/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.8429 - val_loss: 14.1133
Epoch 14/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.7566 - val_loss: 10.1010
Epoch 15/20
1471/1471 [==============================] - 6s 4ms/step - loss: 21.5854 - val_loss: 6.9462
2942/2942 [==============================] - 3s 1ms/step
368/368 [==============================] - 0s 1ms/step
368/368 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 12.632179260253906
Evaluación Validación MAE: 6.946190357208252
Evaluación Prueba MAE: 12.303682327270508
Entrenamiento RMSE: 26.462608337402344
Validación RMSE: 8.469077110290527
Prueba RMSE: 13.66413402557373
Después de realizar multiples pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer pruebas con cada una de ellas se debe a que se quería inferir si el uso de alguna de las características de entrada ofrecía unos resultados muy distintos, pero después de llevar a cabo cada una de las características de entrada por separado se puede determinar que todas dan resultados similares lo cual hace pensar que juntar todas puede dar mejores resultados.
Las arquitecturas LSTM utilizadas son:
Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla vacía
tabla = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement',
'Measurement + In_Range',
'Measurement + Hypo_Hyper',
'Measurement + Hour_of_day',
'Measurement + Day_of_week',
'Todas combinaciones'
])
# Definir los valores en cada posición
tabla.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla.loc[1, 'Measurement'] = train_eval_measurement_1
tabla.loc[2, 'Measurement'] = val_eval_measurement_1
tabla.loc[3, 'Measurement'] = test_eval_measurement_1
tabla.loc[4, 'Measurement'] = train_rmse_measurement_1
tabla.loc[5, 'Measurement'] = val_rmse_measurement_1
tabla.loc[6, 'Measurement'] = test_rmse_measurement_1
tabla.loc[1, 'Measurement + In_Range'] = train_eval_measurement_in_range_1
tabla.loc[2, 'Measurement + In_Range'] = val_eval_measurement_in_range_1
tabla.loc[3, 'Measurement + In_Range'] = test_eval_measurement_in_range_1
tabla.loc[4, 'Measurement + In_Range'] = train_rmse_measurement_in_range_1
tabla.loc[5, 'Measurement + In_Range'] = val_rmse_measurement_in_range_1
tabla.loc[6, 'Measurement + In_Range'] = test_rmse_measurement_in_range_1
tabla.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1
tabla.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1
tabla.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1
tabla.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1
tabla.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1
tabla.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1
tabla.loc[1, 'Measurement + Hour_of_day'] = train_eval_measurement_hour_of_day_1
tabla.loc[2, 'Measurement + Hour_of_day'] = val_eval_measurement_hour_of_day_1
tabla.loc[3, 'Measurement + Hour_of_day'] = test_eval_measurement_hour_of_day_1
tabla.loc[4, 'Measurement + Hour_of_day'] = train_rmse_measurement_hour_of_day_1
tabla.loc[5, 'Measurement + Hour_of_day'] = val_rmse_measurement_hour_of_day_1
tabla.loc[6, 'Measurement + Hour_of_day'] = test_rmse_measurement_hour_of_day_1
tabla.loc[1, 'Measurement + Day_of_week'] = train_eval_measurement_day_of_week_1
tabla.loc[2, 'Measurement + Day_of_week'] = val_eval_measurement_day_of_week_1
tabla.loc[3, 'Measurement + Day_of_week'] = test_eval_measurement_day_of_week_1
tabla.loc[4, 'Measurement + Day_of_week'] = train_rmse_measurement_day_of_week_1
tabla.loc[5, 'Measurement + Day_of_week'] = val_rmse_measurement_day_of_week_1
tabla.loc[6, 'Measurement + Day_of_week'] = test_rmse_measurement_day_of_week_1
tabla.loc[1, 'Todas combinaciones'] = train_eval_measurement_combined_1
tabla.loc[2, 'Todas combinaciones'] = val_eval_measurement_combined_1
tabla.loc[3, 'Todas combinaciones'] = test_eval_measurement_combined_1
tabla.loc[4, 'Todas combinaciones'] = train_rmse_measurement_combined_1
tabla.loc[5, 'Todas combinaciones'] = val_rmse_measurement_combined_1
tabla.loc[6, 'Todas combinaciones'] = test_rmse_measurement_combined_1
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child)', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement | Measurement + In_Range | Measurement + Hypo_Hyper | Measurement + Hour_of_day | Measurement + Day_of_week | Todas combinaciones |
|---|---|---|---|---|---|---|
| Entrenamiento MAE | 3.397733 | 1.654387 | 1.972212 | 3.570218 | 3.097968 | 2.390277 |
| Validación MAE | 1.461310 | 0.301576 | 0.638532 | 1.417756 | 1.763577 | 0.685511 |
| Test MAE | 3.663066 | 0.533755 | 1.057926 | 1.930212 | 1.531365 | 1.236005 |
| Entrenamiento RMSE | 9.165556 | 6.209878 | 6.278009 | 7.898558 | 6.313864 | 6.299334 |
| Validación RMSE | 2.685107 | 1.307544 | 1.441762 | 2.019634 | 2.127568 | 1.504173 |
| Test RMSE | 5.405537 | 1.662802 | 1.962343 | 3.149079 | 2.175009 | 2.051300 |
import pandas as pd
from IPython.display import HTML, display
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla.copy()
cols_to_convert = ['Measurement', 'Measurement + In_Range', 'Measurement + Hypo_Hyper',
'Measurement + Hour_of_day', 'Measurement + Day_of_week', 'Todas combinaciones']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
new_tabla1 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
new_tabla1 = pd.concat([new_tabla1, pd.DataFrame([row])])
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>MEJOR ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = new_tabla1.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|
| Entrenamiento MAE | 1.654387 | Measurement + In_Range |
| Validación MAE | 0.301576 | Measurement + In_Range |
| Test MAE | 0.533755 | Measurement + In_Range |
| Entrenamiento RMSE | 6.209878 | Measurement + In_Range |
| Validación RMSE | 1.307544 | Measurement + In_Range |
| Test RMSE | 1.662802 | Measurement + In_Range |
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla vacía
tabla = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement',
'Measurement + In_Range',
'Measurement + Hypo_Hyper',
'Measurement + Hour_of_day',
'Measurement + Day_of_week',
'Todas combinaciones'
])
# Definir los valores en cada posición
tabla.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla.loc[1, 'Measurement'] = train_eval_measurement_2
tabla.loc[2, 'Measurement'] = val_eval_measurement_2
tabla.loc[3, 'Measurement'] = test_eval_measurement_2
tabla.loc[4, 'Measurement'] = train_rmse_measurement_2
tabla.loc[5, 'Measurement'] = val_rmse_measurement_2
tabla.loc[6, 'Measurement'] = test_rmse_measurement_2
tabla.loc[1, 'Measurement + In_Range'] = train_eval_measurement_in_range_2
tabla.loc[2, 'Measurement + In_Range'] = val_eval_measurement_in_range_2
tabla.loc[3, 'Measurement + In_Range'] = test_eval_measurement_in_range_2
tabla.loc[4, 'Measurement + In_Range'] = train_rmse_measurement_in_range_2
tabla.loc[5, 'Measurement + In_Range'] = val_rmse_measurement_in_range_2
tabla.loc[6, 'Measurement + In_Range'] = test_rmse_measurement_in_range_2
tabla.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2
tabla.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2
tabla.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2
tabla.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2
tabla.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2
tabla.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2
tabla.loc[1, 'Measurement + Hour_of_day'] = train_eval_measurement_hour_of_day_2
tabla.loc[2, 'Measurement + Hour_of_day'] = val_eval_measurement_hour_of_day_2
tabla.loc[3, 'Measurement + Hour_of_day'] = test_eval_measurement_hour_of_day_2
tabla.loc[4, 'Measurement + Hour_of_day'] = train_rmse_measurement_hour_of_day_2
tabla.loc[5, 'Measurement + Hour_of_day'] = val_rmse_measurement_hour_of_day_2
tabla.loc[6, 'Measurement + Hour_of_day'] = test_rmse_measurement_hour_of_day_2
tabla.loc[1, 'Measurement + Day_of_week'] = train_eval_measurement_day_of_week_2
tabla.loc[2, 'Measurement + Day_of_week'] = val_eval_measurement_day_of_week_2
tabla.loc[3, 'Measurement + Day_of_week'] = test_eval_measurement_day_of_week_2
tabla.loc[4, 'Measurement + Day_of_week'] = train_rmse_measurement_day_of_week_2
tabla.loc[5, 'Measurement + Day_of_week'] = val_rmse_measurement_day_of_week_2
tabla.loc[6, 'Measurement + Day_of_week'] = test_rmse_measurement_day_of_week_2
tabla.loc[1, 'Todas combinaciones'] = train_eval_measurement_combined_2
tabla.loc[2, 'Todas combinaciones'] = val_eval_measurement_combined_2
tabla.loc[3, 'Todas combinaciones'] = test_eval_measurement_combined_2
tabla.loc[4, 'Todas combinaciones'] = train_rmse_measurement_combined_2
tabla.loc[5, 'Todas combinaciones'] = val_rmse_measurement_combined_2
tabla.loc[6, 'Todas combinaciones'] = test_rmse_measurement_combined_2
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child)', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement | Measurement + In_Range | Measurement + Hypo_Hyper | Measurement + Hour_of_day | Measurement + Day_of_week | Todas combinaciones |
|---|---|---|---|---|---|---|
| Entrenamiento MAE | 1.675128 | 1.232212 | 1.083968 | 1.998371 | 1.669242 | 2.289662 |
| Validación MAE | 0.905134 | 0.556094 | 0.271668 | 1.057032 | 0.704422 | 0.824245 |
| Test MAE | 0.755180 | 0.752238 | 0.231488 | 1.351160 | 0.774629 | 0.658146 |
| Entrenamiento RMSE | 4.604982 | 4.563739 | 4.527636 | 5.443112 | 5.377539 | 6.420724 |
| Validación RMSE | 1.474888 | 1.279399 | 1.200419 | 1.727100 | 1.490385 | 1.827995 |
| Test RMSE | 1.422599 | 1.383229 | 1.222260 | 1.923753 | 1.552870 | 1.839697 |
import pandas as pd
from IPython.display import HTML, display
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla.copy()
cols_to_convert = ['Measurement', 'Measurement + In_Range', 'Measurement + Hypo_Hyper',
'Measurement + Hour_of_day', 'Measurement + Day_of_week', 'Todas combinaciones']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
new_tabla2 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
new_tabla2 = pd.concat([new_tabla2, pd.DataFrame([row])])
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>MEJOR ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = new_tabla2.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|
| Entrenamiento MAE | 1.083968 | Measurement + Hypo_Hyper |
| Validación MAE | 0.271668 | Measurement + Hypo_Hyper |
| Test MAE | 0.231488 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 4.527636 | Measurement + Hypo_Hyper |
| Validación RMSE | 1.200419 | Measurement + Hypo_Hyper |
| Test RMSE | 1.222260 | Measurement + Hypo_Hyper |
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla vacía
tabla = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement',
'Measurement + In_Range',
'Measurement + Hypo_Hyper',
'Measurement + Hour_of_day',
'Measurement + Day_of_week',
'Todas combinaciones'
])
# Definir los valores en cada posición
tabla.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla.loc[1, 'Measurement'] = train_eval_measurement_3
tabla.loc[2, 'Measurement'] = val_eval_measurement_3
tabla.loc[3, 'Measurement'] = test_eval_measurement_3
tabla.loc[4, 'Measurement'] = train_rmse_measurement_3
tabla.loc[5, 'Measurement'] = val_rmse_measurement_3
tabla.loc[6, 'Measurement'] = test_rmse_measurement_3
tabla.loc[1, 'Measurement + In_Range'] = train_eval_measurement_in_range_3
tabla.loc[2, 'Measurement + In_Range'] = val_eval_measurement_in_range_3
tabla.loc[3, 'Measurement + In_Range'] = test_eval_measurement_in_range_3
tabla.loc[4, 'Measurement + In_Range'] = train_rmse_measurement_in_range_3
tabla.loc[5, 'Measurement + In_Range'] = val_rmse_measurement_in_range_3
tabla.loc[6, 'Measurement + In_Range'] = test_rmse_measurement_in_range_3
tabla.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3
tabla.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3
tabla.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3
tabla.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3
tabla.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3
tabla.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3
tabla.loc[1, 'Measurement + Hour_of_day'] = train_eval_measurement_hour_of_day_3
tabla.loc[2, 'Measurement + Hour_of_day'] = val_eval_measurement_hour_of_day_3
tabla.loc[3, 'Measurement + Hour_of_day'] = test_eval_measurement_hour_of_day_3
tabla.loc[4, 'Measurement + Hour_of_day'] = train_rmse_measurement_hour_of_day_3
tabla.loc[5, 'Measurement + Hour_of_day'] = val_rmse_measurement_hour_of_day_3
tabla.loc[6, 'Measurement + Hour_of_day'] = test_rmse_measurement_hour_of_day_3
tabla.loc[1, 'Measurement + Day_of_week'] = train_eval_measurement_day_of_week_3
tabla.loc[2, 'Measurement + Day_of_week'] = val_eval_measurement_day_of_week_3
tabla.loc[3, 'Measurement + Day_of_week'] = test_eval_measurement_day_of_week_3
tabla.loc[4, 'Measurement + Day_of_week'] = train_rmse_measurement_day_of_week_3
tabla.loc[5, 'Measurement + Day_of_week'] = val_rmse_measurement_day_of_week_3
tabla.loc[6, 'Measurement + Day_of_week'] = test_rmse_measurement_day_of_week_3
tabla.loc[1, 'Todas combinaciones'] = train_eval_measurement_combined_3
tabla.loc[2, 'Todas combinaciones'] = val_eval_measurement_combined_3
tabla.loc[3, 'Todas combinaciones'] = test_eval_measurement_combined_3
tabla.loc[4, 'Todas combinaciones'] = train_rmse_measurement_combined_3
tabla.loc[5, 'Todas combinaciones'] = val_rmse_measurement_combined_3
tabla.loc[6, 'Todas combinaciones'] = test_rmse_measurement_combined_3
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child)', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement | Measurement + In_Range | Measurement + Hypo_Hyper | Measurement + Hour_of_day | Measurement + Day_of_week | Todas combinaciones |
|---|---|---|---|---|---|---|
| Entrenamiento MAE | 12.524332 | 7.778633 | 7.922630 | 5.086625 | 10.655787 | 12.632179 |
| Validación MAE | 12.698191 | 4.523032 | 4.220335 | 3.953882 | 7.297052 | 6.946190 |
| Test MAE | 18.400576 | 9.269299 | 6.241282 | 6.964336 | 13.426828 | 12.303682 |
| Entrenamiento RMSE | 15.802562 | 10.855287 | 10.941482 | 7.240564 | 15.592890 | 26.462608 |
| Validación RMSE | 13.540171 | 5.989138 | 5.068966 | 4.714972 | 8.962604 | 8.469077 |
| Test RMSE | 19.128107 | 10.361228 | 6.475054 | 7.407691 | 14.679519 | 13.664134 |
import pandas as pd
from IPython.display import HTML, display
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla.copy()
cols_to_convert = ['Measurement', 'Measurement + In_Range', 'Measurement + Hypo_Hyper',
'Measurement + Hour_of_day', 'Measurement + Day_of_week', 'Todas combinaciones']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
new_tabla3 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
new_tabla3 = pd.concat([new_tabla3, pd.DataFrame([row])])
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>MEJOR ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = new_tabla3.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|
| Entrenamiento MAE | 5.086625 | Measurement + Hour_of_day |
| Validación MAE | 3.953882 | Measurement + Hour_of_day |
| Test MAE | 6.241282 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 7.240564 | Measurement + Hour_of_day |
| Validación RMSE | 4.714972 | Measurement + Hour_of_day |
| Test RMSE | 6.475054 | Measurement + Hypo_Hyper |
# Combina las tablas horizontalmente
combined_table = pd.concat([new_tabla1, new_tabla2, new_tabla3], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 1.654387 | Measurement + In_Range | Entrenamiento MAE | 1.083968 | Measurement + Hypo_Hyper | Entrenamiento MAE | 5.086625 | Measurement + Hour_of_day |
| Validación MAE | 0.301576 | Measurement + In_Range | Validación MAE | 0.271668 | Measurement + Hypo_Hyper | Validación MAE | 3.953882 | Measurement + Hour_of_day |
| Test MAE | 0.533755 | Measurement + In_Range | Test MAE | 0.231488 | Measurement + Hypo_Hyper | Test MAE | 6.241282 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 6.209878 | Measurement + In_Range | Entrenamiento RMSE | 4.527636 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 7.240564 | Measurement + Hour_of_day |
| Validación RMSE | 1.307544 | Measurement + In_Range | Validación RMSE | 1.200419 | Measurement + Hypo_Hyper | Validación RMSE | 4.714972 | Measurement + Hour_of_day |
| Test RMSE | 1.662802 | Measurement + In_Range | Test RMSE | 1.222260 | Measurement + Hypo_Hyper | Test RMSE | 6.475054 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = new_tabla1.loc[new_tabla1['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not new_tabla1.loc[new_tabla1['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = new_tabla1.loc[new_tabla1['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = new_tabla2.loc[new_tabla2['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not new_tabla2.loc[new_tabla2['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = new_tabla2.loc[new_tabla2['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = new_tabla3.loc[new_tabla3['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not new_tabla3.loc[new_tabla3['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = new_tabla3.loc[new_tabla3['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_22 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_22}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.083968 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.271668 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.231488 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 4.527636 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 1.200419 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.222260 | Measurement + Hypo_Hyper | 2 |
La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.
La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:
El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.
En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados e incluso se puede apreciar que las características de entrada con mejores resultados son Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2, utilizando las características de entrada Measurement + Hypo_Hyper.
Este análisis de resultados concluye que los próximos análisis se hagan con las características de entrada Measurement + Hypo_Hyper ya que han obtenido globalmente unos mejores resultados en rendimiento de predicción y precisión al entrenamiento y generalización a nuevos datos.
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-11-06 06:45:00 | 134 | False | False |
| 2018-11-06 07:00:00 | 134 | False | False |
| 2018-11-06 07:15:00 | 139 | False | False |
| 2018-11-06 07:30:00 | 145 | False | False |
| 2018-11-06 07:45:00 | 147 | False | False |
| ... | ... | ... | ... |
| 2022-03-08 11:30:00 | 179 | False | False |
| 2022-03-08 12:30:00 | 173 | False | False |
| 2022-03-08 12:45:00 | 171 | False | False |
| 2022-03-08 13:15:00 | 165 | False | False |
| 2022-03-15 23:46:00 | 155 | False | False |
10409 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_48"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv1d_15 (Conv1D) (None, 6, 32) 320
batch_normalization_30 (Bat (None, 6, 32) 128
chNormalization)
max_pooling1d_15 (MaxPoolin (None, 3, 32) 0
g1D)
dropout_30 (Dropout) (None, 3, 32) 0
lstm_58 (LSTM) (None, 64) 24832
batch_normalization_31 (Bat (None, 64) 256
chNormalization)
dropout_31 (Dropout) (None, 64) 0
dense_48 (Dense) (None, 1) 65
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1471/1471 [==============================] - 4s 2ms/step - loss: 48.2772 - val_loss: 23.9773 Epoch 2/20 1471/1471 [==============================] - 3s 2ms/step - loss: 22.6054 - val_loss: 10.5451 Epoch 3/20 1471/1471 [==============================] - 3s 2ms/step - loss: 21.9258 - val_loss: 8.8166 Epoch 4/20 1471/1471 [==============================] - 3s 2ms/step - loss: 21.5737 - val_loss: 6.1133 Epoch 5/20 1471/1471 [==============================] - 3s 2ms/step - loss: 21.1827 - val_loss: 12.7950 Epoch 6/20 1471/1471 [==============================] - 3s 2ms/step - loss: 21.0718 - val_loss: 12.1782 Epoch 7/20 1471/1471 [==============================] - 3s 2ms/step - loss: 21.0374 - val_loss: 6.8255
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN = train_eval
val_eval_measurement_hypo_hype_3_CRNN = val_eval
test_eval_measurement_hypo_hype_3_CRNN = test_eval
train_rmse_measurement_hypo_hype_3_CRNN = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN = test_rmse
2942/2942 [==============================] - 2s 649us/step 368/368 [==============================] - 0s 662us/step 368/368 [==============================] - 0s 659us/step Evaluación Entrenamiento MAE: 11.181082725524902 Evaluación Validación MAE: 6.825511455535889 Evaluación Prueba MAE: 7.6780290603637695 Entrenamiento RMSE: 17.095659255981445 Validación RMSE: 8.660697937011719 Prueba RMSE: 11.64884090423584
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 9ms/step
Datos reales Datos predichos
date
2022-03-16 00:01:00 NaN 166.588135
2022-03-16 00:16:00 NaN 164.287796
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_2_CRNN = train_eval
val_eval_measurement_hypo_hype_2_CRNN = val_eval
test_eval_measurement_hypo_hype_2_CRNN = test_eval
train_rmse_measurement_hypo_hype_2_CRNN = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1471/1471 [==============================] - 3s 2ms/step - loss: 136.9881 - val_loss: 126.6721 Epoch 2/20 1471/1471 [==============================] - 2s 1ms/step - loss: 108.5541 - val_loss: 99.0082 Epoch 3/20 1471/1471 [==============================] - 2s 1ms/step - loss: 81.0333 - val_loss: 71.5418 Epoch 4/20 1471/1471 [==============================] - 2s 2ms/step - loss: 53.8995 - val_loss: 44.2691 Epoch 5/20 1471/1471 [==============================] - 2s 1ms/step - loss: 29.1893 - val_loss: 19.4732 Epoch 6/20 1471/1471 [==============================] - 2s 1ms/step - loss: 19.0227 - val_loss: 12.2275 Epoch 7/20 1471/1471 [==============================] - 2s 1ms/step - loss: 17.8867 - val_loss: 12.0720 Epoch 8/20 1471/1471 [==============================] - 2s 1ms/step - loss: 17.8813 - val_loss: 12.3091 Epoch 9/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.2959 - val_loss: 9.2642 Epoch 10/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.0885 - val_loss: 8.9684 Epoch 11/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.0371 - val_loss: 8.3480 Epoch 12/20 1471/1471 [==============================] - 2s 2ms/step - loss: 16.0685 - val_loss: 8.3343 Epoch 13/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.0704 - val_loss: 8.3280 Epoch 14/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.0706 - val_loss: 8.3287 Epoch 15/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.0709 - val_loss: 8.3294 Epoch 16/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.0706 - val_loss: 8.3301 2942/2942 [==============================] - 2s 536us/step 368/368 [==============================] - 0s 536us/step 368/368 [==============================] - 0s 550us/step Evaluación Entrenamiento MAE: 16.1252498626709 Evaluación Validación MAE: 8.330071449279785 Evaluación Prueba MAE: 17.305015563964844 Entrenamiento RMSE: 22.40139389038086 Validación RMSE: 11.44087028503418 Prueba RMSE: 19.776081085205078
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_22[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_1_CRNN = train_eval
val_eval_measurement_hypo_hype_1_CRNN = val_eval
test_eval_measurement_hypo_hype_1_CRNN = test_eval
train_rmse_measurement_hypo_hype_1_CRNN = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1471/1471 [==============================] - 3s 1ms/step - loss: 135.6173 - val_loss: 124.1344 Epoch 2/20 1471/1471 [==============================] - 2s 1ms/step - loss: 104.4509 - val_loss: 93.3071 Epoch 3/20 1471/1471 [==============================] - 2s 1ms/step - loss: 73.7337 - val_loss: 62.6001 Epoch 4/20 1471/1471 [==============================] - 2s 1ms/step - loss: 43.7654 - val_loss: 32.3940 Epoch 5/20 1471/1471 [==============================] - 2s 1ms/step - loss: 20.9529 - val_loss: 12.1744 Epoch 6/20 1471/1471 [==============================] - 2s 1ms/step - loss: 18.1850 - val_loss: 11.2732 Epoch 7/20 1471/1471 [==============================] - 2s 1ms/step - loss: 17.2536 - val_loss: 10.1044 Epoch 8/20 1471/1471 [==============================] - 2s 1ms/step - loss: 17.5440 - val_loss: 10.9213 Epoch 9/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.7779 - val_loss: 9.7612 Epoch 10/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.0673 - val_loss: 8.4114 Epoch 11/20 1471/1471 [==============================] - 2s 1ms/step - loss: 17.7470 - val_loss: 11.4089 Epoch 12/20 1471/1471 [==============================] - 2s 1ms/step - loss: 16.6330 - val_loss: 9.7207 Epoch 13/20 1471/1471 [==============================] - 2s 1ms/step - loss: 17.3310 - val_loss: 10.8876 2942/2942 [==============================] - 2s 510us/step 368/368 [==============================] - 0s 555us/step 368/368 [==============================] - 0s 532us/step Evaluación Entrenamiento MAE: 16.346696853637695 Evaluación Validación MAE: 10.887613296508789 Evaluación Prueba MAE: 21.2574462890625 Entrenamiento RMSE: 22.930130004882812 Validación RMSE: 13.952215194702148 Prueba RMSE: 23.73246955871582
Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas CRNN utilizadas son:
Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reúne todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_CRNN vacía
tabla_1_CRNN = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_CRNN.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_CRNN.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_CRNN.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN
tabla_1_CRNN.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 16.346697 |
| Validación MAE | 10.887613 |
| Test MAE | 21.257446 |
| Entrenamiento RMSE | 22.930130 |
| Validación RMSE | 13.952215 |
| Test RMSE | 23.732470 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_CRNN = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_CRNN = pd.concat([tabla_1_CRNN, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_2_CRNN vacía
tabla_2_CRNN = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_CRNN.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_CRNN.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_CRNN.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN
tabla_2_CRNN.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 16.125250 |
| Validación MAE | 8.330071 |
| Test MAE | 17.305016 |
| Entrenamiento RMSE | 22.401394 |
| Validación RMSE | 11.440870 |
| Test RMSE | 19.776081 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_CRNN = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_CRNN = pd.concat([tabla_2_CRNN, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_CRNN vacía
tabla_3_CRNN = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_CRNN.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_CRNN.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_CRNN.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN
tabla_3_CRNN.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 11.181083 |
| Validación MAE | 6.825511 |
| Test MAE | 7.678029 |
| Entrenamiento RMSE | 17.095659 |
| Validación RMSE | 8.660698 |
| Test RMSE | 11.648841 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_CRNN = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_CRNN = pd.concat([tabla_3_CRNN, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN, tabla_2_CRNN, tabla_3_CRNN], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 16.346697 | Measurement + Hypo_Hyper | Entrenamiento MAE | 16.125250 | Measurement + Hypo_Hyper | Entrenamiento MAE | 11.181083 | Measurement + Hypo_Hyper |
| Validación MAE | 10.887613 | Measurement + Hypo_Hyper | Validación MAE | 8.330071 | Measurement + Hypo_Hyper | Validación MAE | 6.825511 | Measurement + Hypo_Hyper |
| Test MAE | 21.257446 | Measurement + Hypo_Hyper | Test MAE | 17.305016 | Measurement + Hypo_Hyper | Test MAE | 7.678029 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 22.930130 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 22.401394 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 17.095659 | Measurement + Hypo_Hyper |
| Validación RMSE | 13.952215 | Measurement + Hypo_Hyper | Validación RMSE | 11.440870 | Measurement + Hypo_Hyper | Validación RMSE | 8.660698 | Measurement + Hypo_Hyper |
| Test RMSE | 23.732470 | Measurement + Hypo_Hyper | Test RMSE | 19.776081 | Measurement + Hypo_Hyper | Test RMSE | 11.648841 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_CRNN.loc[tabla_1_CRNN['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_CRNN.loc[tabla_1_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_CRNN.loc[tabla_1_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_CRNN.loc[tabla_2_CRNN['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_CRNN.loc[tabla_2_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_CRNN.loc[tabla_2_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_CRNN.loc[tabla_3_CRNN['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_CRNN.loc[tabla_3_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_CRNN.loc[tabla_3_CRNN['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_22 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_22}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 11.181083 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 6.825511 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 7.678029 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 17.095659 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 8.660698 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 11.648841 | Measurement + Hypo_Hyper | 3 |
La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.
Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.
La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.
En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.
from IPython.display import display, HTML
html_tables = f"""
<style>
.table-wrapper {{
display: inline-block;
font-size: 14px;
vertical-align: top;
margin-right: 20px;
}}
.table-wrapper table {{
font-size: 14px;
}}
.table-wrapper th, .table-wrapper td {{
width: 100px;
text-align: center;
}}
.table-wrapper h3 {{
text-align: center;
font-size: 18px;
color: blue;
}}
h2 {{
text-align: center;
}}
</style>
<h2>Paciente 22</h2>
<div class="table-wrapper">
<h3>LSTM</h3>
{LSTM_22}
</div>
<div class="table-wrapper">
<h3>CRNN</h3>
{CRNN_22}
</div>
"""
display(HTML(html_tables))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.083968 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.271668 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.231488 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 4.527636 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 1.200419 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.222260 | Measurement + Hypo_Hyper | 2 |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 11.181083 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 6.825511 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 7.678029 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 17.095659 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 8.660698 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 11.648841 | Measurement + Hypo_Hyper | 3 |
from IPython.display import display, HTML
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN': 1, 'tabla_2_CRNN': 2, 'tabla_3_CRNN': 3}
tables_arch2 = {'new_tabla1': 1, 'new_tabla2': 2, 'new_tabla3': 3}
# Para cada fila
for row in rows:
min_vals = []
# Arquitectura 1
for table_name, arch in tables_arch1.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'CRNN', arch))
# Arquitectura 2
for table_name, arch in tables_arch2.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'LSTM', arch))
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [min_arch],
'Arquitectura (Hiperparámetros)': [min_num]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 22)</span></center>"
display(HTML(html_text))
# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()
# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))
# Nombrando
styled_result1 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result1 = styled_result1.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_22.style.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"
# Muestra el HTML
display(HTML(html))
# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))
# Mostrar la figura
display(fig_hypo_hype_2_LSTM_22)
# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_22.style.set_properties(**{'text-align': 'center'}).hide(axis='index')
# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"
# Mostrar la tabla centrada
display(HTML(table_html))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.083968 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.271668 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.231488 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 4.527636 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 1.200419 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.222260 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-03-16 00:01:00 | nan | 156.141495 |
| 2022-03-16 00:16:00 | nan | 157.410568 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 11758 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
Los resultados nos aclara que la mejor opción para el paciente 22 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.
Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):
La conclusión final para el paciente 22 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hour_of_day para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.
La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 24 debido a que tras un análisis extenso se ha comprobado que es el 1º con mayor número de mediciones.
Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.
Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:
Arquitectura de hiperparámetros de LSTM:
Arquitectura de hiperparámetros de CRNN:
La característica de entrada va a ser: 'Measurement + Hypo_Hyper'
# Filtrar los datos para el paciente 24
df_paciente_24 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 24]
# Imprimir los resultados
df_paciente_24
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-07-13 10:15:00 | 24 | 2018-07-13 | 1900-01-01 10:15:00 | 142 | False | True | False | 10 | 4 |
| 2018-07-13 10:30:00 | 24 | 2018-07-13 | 1900-01-01 10:30:00 | 142 | True | False | False | 10 | 4 |
| 2018-07-13 10:45:00 | 24 | 2018-07-13 | 1900-01-01 10:45:00 | 153 | True | False | False | 10 | 4 |
| 2018-07-13 11:00:00 | 24 | 2018-07-13 | 1900-01-01 11:00:00 | 160 | True | False | False | 11 | 4 |
| 2018-07-13 11:15:00 | 24 | 2018-07-13 | 1900-01-01 11:15:00 | 162 | False | False | True | 11 | 4 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-12 05:30:00 | 24 | 2022-03-12 | 1900-01-01 05:30:00 | 156 | True | False | False | 5 | 5 |
| 2022-03-12 06:30:00 | 24 | 2022-03-12 | 1900-01-01 06:30:00 | 154 | True | False | False | 6 | 5 |
| 2022-03-12 06:45:00 | 24 | 2022-03-12 | 1900-01-01 06:45:00 | 154 | True | False | False | 6 | 5 |
| 2022-03-12 07:45:00 | 24 | 2022-03-12 | 1900-01-01 07:45:00 | 153 | True | False | False | 7 | 5 |
| 2022-03-12 08:30:00 | 24 | 2022-03-12 | 1900-01-01 08:30:00 | 158 | False | True | False | 8 | 5 |
11987 rows × 9 columns
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-07-13 10:15:00 | 142 | True | False |
| 2018-07-13 10:30:00 | 142 | False | False |
| 2018-07-13 10:45:00 | 153 | False | False |
| 2018-07-13 11:00:00 | 160 | False | False |
| 2018-07-13 11:15:00 | 162 | False | True |
| ... | ... | ... | ... |
| 2022-03-12 05:30:00 | 156 | False | False |
| 2022-03-12 06:30:00 | 154 | False | False |
| 2022-03-12 06:45:00 | 154 | False | False |
| 2022-03-12 07:45:00 | 153 | False | False |
| 2022-03-12 08:30:00 | 158 | True | False |
11987 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_21"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_27 (LSTM) (None, 64) 17408
dense_21 (Dense) (None, 1) 65
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1606/1606 [==============================] - 5s 3ms/step - loss: 3.3317 - val_loss: 0.7470 Epoch 2/20 1606/1606 [==============================] - 4s 3ms/step - loss: 2.4871 - val_loss: 1.6190 Epoch 3/20 1606/1606 [==============================] - 4s 3ms/step - loss: 2.0046 - val_loss: 0.1555 Epoch 4/20 1606/1606 [==============================] - 4s 3ms/step - loss: 2.0574 - val_loss: 0.4030 Epoch 5/20 1606/1606 [==============================] - 4s 3ms/step - loss: 1.9047 - val_loss: 1.4204 Epoch 6/20 1606/1606 [==============================] - 4s 3ms/step - loss: 1.8448 - val_loss: 0.7944
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_24 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_24 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_24 = test_eval
train_rmse_measurement_hypo_hype_2_LSTM_24 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_24 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_24 = test_rmse
3211/3211 [==============================] - 3s 799us/step 402/402 [==============================] - 0s 802us/step 402/402 [==============================] - 0s 798us/step Evaluación Entrenamiento MAE: 1.848578929901123 Evaluación Validación MAE: 0.7943907380104065 Evaluación Prueba MAE: 0.8569515347480774 Entrenamiento RMSE: 5.513991832733154 Validación RMSE: 0.9274844527244568 Prueba RMSE: 0.9750375151634216
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
# Almacenar dato
future_predictions_hypo_hype_2_LSTM_24 = future_predictions
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-03-12 08:45:00 NaN 156.134811
2022-03-12 09:00:00 NaN 155.769943
def clarke_error_grid(ref_values, pred_values):
assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))
if max(ref_values) > 400 or max(pred_values) > 400:
print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
if min(ref_values) < 0 or min(pred_values) < 0:
print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values), min(pred_values)))
plt.clf()
plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
plt.title("Clarke Error Grid")
plt.xlabel("Reference Concentration (mg/dl)")
plt.ylabel("Prediction Concentration (mg/dl)")
plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.gca().set_facecolor('white')
plt.gca().set_xlim([0, 400])
plt.gca().set_ylim([0, 400])
plt.gca().set_aspect((400)/(400))
plt.plot([0, 400], [0, 400], ':', c='black')
plt.plot([0, 175/3], [70, 70], '-', c='black')
plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
plt.plot([70, 70], [84, 400],'-', c='black')
plt.plot([0, 70], [180, 180], '-', c='black')
plt.plot([70, 290], [180, 400],'-', c='black')
plt.plot([70, 70], [0, 56], '-', c='black')
plt.plot([70, 400], [56, 320],'-', c='black')
plt.plot([180, 180], [0, 70], '-', c='black')
plt.plot([180, 400], [70, 70], '-', c='black')
plt.plot([240, 240], [70, 180],'-', c='black')
plt.plot([240, 400], [180, 180], '-', c='black')
plt.plot([130, 180], [0, 70], '-', c='black')
plt.text(30, 15, "A", fontsize=15)
plt.text(370, 260, "B", fontsize=15)
plt.text(280, 370, "B", fontsize=15)
plt.text(160, 370, "C", fontsize=15)
plt.text(160, 15, "C", fontsize=15)
plt.text(30, 140, "D", fontsize=15)
plt.text(370, 120, "D", fontsize=15)
plt.text(30, 370, "E", fontsize=15)
plt.text(370, 15, "E", fontsize=15)
zone = [0] * 5
for i in range(len(ref_values)):
if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
zone[0] += 1 # Zone A
elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
zone[4] += 1 # Zone E
elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
zone[2] += 1 # Zone C
elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
zone[3] += 1 # Zone D
else:
zone[1] += 1 # Zone B
return plt, zone
ref_values = y_test
pred_values = y_pred_test
plt, zone = clarke_error_grid(ref_values, pred_values)
# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_24 = plt.gcf()
# Mostrar la figura
plt.show()
# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})
# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total
# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))
# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_24 = zone_df
# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_24.style.hide(axis="index"))
Conteo de Zonas:
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12836 | 100.00% |
| B | 0 | 0.00% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_24 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_24 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_24 = test_eval
train_rmse_measurement_hypo_hype_1_LSTM_24 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_24 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_24 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_22"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_28 (LSTM) (None, 32) 4608
dense_22 (Dense) (None, 1) 33
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1606/1606 [==============================] - 4s 2ms/step - loss: 136.3816 - val_loss: 124.1663
Epoch 2/20
1606/1606 [==============================] - 3s 2ms/step - loss: 107.1916 - val_loss: 95.4709
Epoch 3/20
1606/1606 [==============================] - 3s 2ms/step - loss: 78.9571 - val_loss: 67.9738
Epoch 4/20
1606/1606 [==============================] - 3s 2ms/step - loss: 52.3246 - val_loss: 41.1366
Epoch 5/20
1606/1606 [==============================] - 3s 2ms/step - loss: 29.9364 - val_loss: 18.8177
Epoch 6/20
1606/1606 [==============================] - 3s 2ms/step - loss: 17.2856 - val_loss: 11.8577
Epoch 7/20
1606/1606 [==============================] - 3s 2ms/step - loss: 10.7866 - val_loss: 6.1048
Epoch 8/20
1606/1606 [==============================] - 3s 2ms/step - loss: 7.9177 - val_loss: 4.4213
Epoch 9/20
1606/1606 [==============================] - 3s 2ms/step - loss: 6.0058 - val_loss: 2.8679
Epoch 10/20
1606/1606 [==============================] - 3s 2ms/step - loss: 4.8831 - val_loss: 1.9169
Epoch 11/20
1606/1606 [==============================] - 3s 2ms/step - loss: 4.3533 - val_loss: 0.8242
Epoch 12/20
1606/1606 [==============================] - 3s 2ms/step - loss: 3.7928 - val_loss: 1.0000
Epoch 13/20
1606/1606 [==============================] - 3s 2ms/step - loss: 3.6041 - val_loss: 0.6903
Epoch 14/20
1606/1606 [==============================] - 3s 2ms/step - loss: 3.1577 - val_loss: 0.3456
Epoch 15/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.9796 - val_loss: 0.2120
Epoch 16/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.6932 - val_loss: 0.8564
Epoch 17/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.6843 - val_loss: 0.5786
Epoch 18/20
1606/1606 [==============================] - 3s 2ms/step - loss: 2.7187 - val_loss: 1.0091
3211/3211 [==============================] - 2s 602us/step
402/402 [==============================] - 0s 605us/step
402/402 [==============================] - 0s 605us/step
Evaluación Entrenamiento MAE: 2.781226634979248
Evaluación Validación MAE: 1.009082317352295
Evaluación Prueba MAE: 1.2069369554519653
Entrenamiento RMSE: 7.843711853027344
Validación RMSE: 1.1686525344848633
Prueba RMSE: 1.3281201124191284
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Crear secuencias de entrenamiento y prueba para el modelo LSTM
def create_sequences(data, seq_length):
xs = []
ys = []
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
# RMSE Interpreta las mismas unidades que la variable objetivo (nivel de glucosa). Identificas el error de unidades de Glucosa.
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_24 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_24 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_24 = test_eval
train_rmse_measurement_hypo_hype_3_LSTM_24 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_24 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_24 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_23"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_29 (LSTM) (None, 8, 32) 4608
batch_normalization_14 (Bat (None, 8, 32) 128
chNormalization)
dropout_14 (Dropout) (None, 8, 32) 0
lstm_30 (LSTM) (None, 64) 24832
batch_normalization_15 (Bat (None, 64) 256
chNormalization)
dropout_15 (Dropout) (None, 64) 0
dense_23 (Dense) (None, 1) 65
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1606/1606 [==============================] - 7s 4ms/step - loss: 45.9360 - val_loss: 13.4667
Epoch 2/20
1606/1606 [==============================] - 6s 4ms/step - loss: 24.6978 - val_loss: 14.5635
Epoch 3/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.8469 - val_loss: 6.0860
Epoch 4/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.8350 - val_loss: 3.7788
Epoch 5/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.3111 - val_loss: 7.8080
Epoch 6/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.2787 - val_loss: 13.5805
Epoch 7/20
1606/1606 [==============================] - 6s 4ms/step - loss: 23.1046 - val_loss: 11.7681
3211/3211 [==============================] - 4s 1ms/step
402/402 [==============================] - 0s 1ms/step
402/402 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 14.309062004089355
Evaluación Validación MAE: 11.768108367919922
Evaluación Prueba MAE: 11.139135360717773
Entrenamiento RMSE: 16.737184524536133
Validación RMSE: 11.954730033874512
Prueba RMSE: 11.243288040161133
Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas LSTM utilizadas son:
Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_LSTM_24 vacía
tabla_1_LSTM_24 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_LSTM_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_LSTM_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_LSTM_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_24
tabla_1_LSTM_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_24
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 2.781227 |
| Validación MAE | 1.009082 |
| Test MAE | 1.206937 |
| Entrenamiento RMSE | 7.843712 |
| Validación RMSE | 1.168653 |
| Test RMSE | 1.328120 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_LSTM_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_LSTM_24 = pd.concat([tabla_1_LSTM_24, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla vacía
tabla_2_LSTM_24 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_LSTM_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_LSTM_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_LSTM_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_24
tabla_2_LSTM_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_24
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 1.848579 |
| Validación MAE | 0.794391 |
| Test MAE | 0.856952 |
| Entrenamiento RMSE | 5.513992 |
| Validación RMSE | 0.927484 |
| Test RMSE | 0.975038 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_LSTM_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_LSTM_24 = pd.concat([tabla_2_LSTM_24, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_LSTM_11 vacía
tabla_3_LSTM_24 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_LSTM_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_LSTM_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_LSTM_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_24
tabla_3_LSTM_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_24
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 14.309062 |
| Validación MAE | 11.768108 |
| Test MAE | 11.139135 |
| Entrenamiento RMSE | 16.737185 |
| Validación RMSE | 11.954730 |
| Test RMSE | 11.243288 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_LSTM_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_LSTM_24 = pd.concat([tabla_3_LSTM_24, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_24, tabla_2_LSTM_24, tabla_3_LSTM_24], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 2.781227 | Measurement + Hypo_Hyper | Entrenamiento MAE | 1.848579 | Measurement + Hypo_Hyper | Entrenamiento MAE | 14.309062 | Measurement + Hypo_Hyper |
| Validación MAE | 1.009082 | Measurement + Hypo_Hyper | Validación MAE | 0.794391 | Measurement + Hypo_Hyper | Validación MAE | 11.768108 | Measurement + Hypo_Hyper |
| Test MAE | 1.206937 | Measurement + Hypo_Hyper | Test MAE | 0.856952 | Measurement + Hypo_Hyper | Test MAE | 11.139135 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 7.843712 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 5.513992 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 16.737185 | Measurement + Hypo_Hyper |
| Validación RMSE | 1.168653 | Measurement + Hypo_Hyper | Validación RMSE | 0.927484 | Measurement + Hypo_Hyper | Validación RMSE | 11.954730 | Measurement + Hypo_Hyper |
| Test RMSE | 1.328120 | Measurement + Hypo_Hyper | Test RMSE | 0.975038 | Measurement + Hypo_Hyper | Test RMSE | 11.243288 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_LSTM_24.loc[tabla_1_LSTM_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_LSTM_24.loc[tabla_1_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_LSTM_24.loc[tabla_1_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_LSTM_24.loc[tabla_2_LSTM_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_LSTM_24.loc[tabla_2_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_LSTM_24.loc[tabla_2_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_LSTM_24.loc[tabla_3_LSTM_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_LSTM_24.loc[tabla_3_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_LSTM_24.loc[tabla_3_LSTM_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_24 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_24}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.848579 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.794391 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.856952 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 5.513992 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 0.927484 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 0.975038 | Measurement + Hypo_Hyper | 2 |
La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.
La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:
El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.
En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-07-13 10:15:00 | 142 | True | False |
| 2018-07-13 10:30:00 | 142 | False | False |
| 2018-07-13 10:45:00 | 153 | False | False |
| 2018-07-13 11:00:00 | 160 | False | False |
| 2018-07-13 11:15:00 | 162 | False | True |
| ... | ... | ... | ... |
| 2022-03-12 05:30:00 | 156 | False | False |
| 2022-03-12 06:30:00 | 154 | False | False |
| 2022-03-12 06:45:00 | 154 | False | False |
| 2022-03-12 07:45:00 | 153 | False | False |
| 2022-03-12 08:30:00 | 158 | True | False |
11987 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_53"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv1d_20 (Conv1D) (None, 6, 32) 320
batch_normalization_40 (Bat (None, 6, 32) 128
chNormalization)
max_pooling1d_20 (MaxPoolin (None, 3, 32) 0
g1D)
dropout_40 (Dropout) (None, 3, 32) 0
lstm_63 (LSTM) (None, 64) 24832
batch_normalization_41 (Bat (None, 64) 256
chNormalization)
dropout_41 (Dropout) (None, 64) 0
dense_53 (Dense) (None, 1) 65
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1606/1606 [==============================] - 5s 2ms/step - loss: 44.9550 - val_loss: 24.7769 Epoch 2/20 1606/1606 [==============================] - 3s 2ms/step - loss: 24.5620 - val_loss: 15.5933 Epoch 3/20 1606/1606 [==============================] - 3s 2ms/step - loss: 24.0151 - val_loss: 6.3546 Epoch 4/20 1606/1606 [==============================] - 3s 2ms/step - loss: 23.5473 - val_loss: 5.2315 Epoch 5/20 1606/1606 [==============================] - 4s 2ms/step - loss: 23.5834 - val_loss: 7.4198 Epoch 6/20 1606/1606 [==============================] - 3s 2ms/step - loss: 23.3172 - val_loss: 8.6975 Epoch 7/20 1606/1606 [==============================] - 3s 2ms/step - loss: 23.3292 - val_loss: 9.7800
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_24 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_24 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_24 = test_eval
train_rmse_measurement_hypo_hype_3_CRNN_24 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_24 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_24 = test_rmse
3211/3211 [==============================] - 2s 664us/step 402/402 [==============================] - 0s 673us/step 402/402 [==============================] - 0s 682us/step Evaluación Entrenamiento MAE: 13.840426445007324 Evaluación Validación MAE: 9.779932975769043 Evaluación Prueba MAE: 14.4535493850708 Entrenamiento RMSE: 15.8035306930542 Validación RMSE: 10.193990707397461 Prueba RMSE: 14.531745910644531
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 9ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-03-12 08:45:00 NaN 178.983139
2022-03-12 09:00:00 NaN 208.545502
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_2_CRNN_24 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_24 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_24 = test_eval
train_rmse_measurement_hypo_hype_2_CRNN_24 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_24 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_24 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1606/1606 [==============================] - 3s 2ms/step - loss: 133.5308 - val_loss: 120.4234 Epoch 2/20 1606/1606 [==============================] - 3s 2ms/step - loss: 98.8586 - val_loss: 82.7842 Epoch 3/20 1606/1606 [==============================] - 3s 2ms/step - loss: 62.5007 - val_loss: 47.4328 Epoch 4/20 1606/1606 [==============================] - 3s 2ms/step - loss: 35.7188 - val_loss: 21.0515 Epoch 5/20 1606/1606 [==============================] - 2s 2ms/step - loss: 28.3419 - val_loss: 16.9870 Epoch 6/20 1606/1606 [==============================] - 2s 1ms/step - loss: 25.8684 - val_loss: 16.2216 Epoch 7/20 1606/1606 [==============================] - 2s 1ms/step - loss: 32.6291 - val_loss: 24.4362 Epoch 8/20 1606/1606 [==============================] - 2s 1ms/step - loss: 32.0655 - val_loss: 20.8216 Epoch 9/20 1606/1606 [==============================] - 2s 1ms/step - loss: 26.2422 - val_loss: 16.9353 3211/3211 [==============================] - 2s 539us/step 402/402 [==============================] - 0s 554us/step 402/402 [==============================] - 0s 564us/step Evaluación Entrenamiento MAE: 22.284160614013672 Evaluación Validación MAE: 16.93526840209961 Evaluación Prueba MAE: 22.816110610961914 Entrenamiento RMSE: 30.645200729370117 Validación RMSE: 21.33058738708496 Prueba RMSE: 24.184709548950195
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_24[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_1_CRNN_24 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_24 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_24 = test_eval
train_rmse_measurement_hypo_hype_1_CRNN_24 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_24 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_24 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1606/1606 [==============================] - 3s 2ms/step - loss: 124.2368 - val_loss: 102.9912 Epoch 2/20 1606/1606 [==============================] - 2s 1ms/step - loss: 77.6768 - val_loss: 57.9350 Epoch 3/20 1606/1606 [==============================] - 2s 1ms/step - loss: 44.4777 - val_loss: 30.9328 Epoch 4/20 1606/1606 [==============================] - 2s 1ms/step - loss: 31.2914 - val_loss: 20.9542 Epoch 5/20 1606/1606 [==============================] - 2s 1ms/step - loss: 26.7813 - val_loss: 16.6654 Epoch 6/20 1606/1606 [==============================] - 2s 1ms/step - loss: 31.2644 - val_loss: 18.1241 Epoch 7/20 1606/1606 [==============================] - 2s 1ms/step - loss: 26.9874 - val_loss: 16.9876 Epoch 8/20 1606/1606 [==============================] - 2s 1ms/step - loss: 27.8359 - val_loss: 12.7023 Epoch 9/20 1606/1606 [==============================] - 2s 1ms/step - loss: 26.6594 - val_loss: 14.6765 Epoch 10/20 1606/1606 [==============================] - 2s 1ms/step - loss: 23.4942 - val_loss: 13.5289 Epoch 11/20 1606/1606 [==============================] - 2s 1ms/step - loss: 22.0483 - val_loss: 15.4298 3211/3211 [==============================] - 2s 516us/step 402/402 [==============================] - 0s 534us/step 402/402 [==============================] - 0s 532us/step Evaluación Entrenamiento MAE: 21.60335922241211 Evaluación Validación MAE: 15.429783821105957 Evaluación Prueba MAE: 21.31039810180664 Entrenamiento RMSE: 29.793907165527344 Validación RMSE: 20.155935287475586 Prueba RMSE: 22.769638061523438
Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas CRNN utilizadas son:
Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_24 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_CRNN_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_CRNN_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_CRNN_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_24
tabla_1_CRNN_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_24
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 21.603359 |
| Validación MAE | 15.429784 |
| Test MAE | 21.310398 |
| Entrenamiento RMSE | 29.793907 |
| Validación RMSE | 20.155935 |
| Test RMSE | 22.769638 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_CRNN_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_CRNN_24 = pd.concat([tabla_1_CRNN_24, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_24 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_CRNN_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_CRNN_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_CRNN_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_24
tabla_2_CRNN_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_24
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 22.284161 |
| Validación MAE | 16.935268 |
| Test MAE | 22.816111 |
| Entrenamiento RMSE | 30.645201 |
| Validación RMSE | 21.330587 |
| Test RMSE | 24.184710 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_CRNN_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_CRNN_24 = pd.concat([tabla_2_CRNN_24, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_24 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_CRNN_24.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_24.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_24.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_CRNN_24.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_24.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_24.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_CRNN_24.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_24
tabla_3_CRNN_24.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_24
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_24.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 13.840426 |
| Validación MAE | 9.779933 |
| Test MAE | 14.453549 |
| Entrenamiento RMSE | 15.803531 |
| Validación RMSE | 10.193991 |
| Test RMSE | 14.531746 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_24.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_CRNN_24 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_CRNN_24 = pd.concat([tabla_3_CRNN_24, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_24, tabla_2_CRNN_24, tabla_3_CRNN_24], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 21.603359 | Measurement + Hypo_Hyper | Entrenamiento MAE | 22.284161 | Measurement + Hypo_Hyper | Entrenamiento MAE | 13.840426 | Measurement + Hypo_Hyper |
| Validación MAE | 15.429784 | Measurement + Hypo_Hyper | Validación MAE | 16.935268 | Measurement + Hypo_Hyper | Validación MAE | 9.779933 | Measurement + Hypo_Hyper |
| Test MAE | 21.310398 | Measurement + Hypo_Hyper | Test MAE | 22.816111 | Measurement + Hypo_Hyper | Test MAE | 14.453549 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 29.793907 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 30.645201 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 15.803531 | Measurement + Hypo_Hyper |
| Validación RMSE | 20.155935 | Measurement + Hypo_Hyper | Validación RMSE | 21.330587 | Measurement + Hypo_Hyper | Validación RMSE | 10.193991 | Measurement + Hypo_Hyper |
| Test RMSE | 22.769638 | Measurement + Hypo_Hyper | Test RMSE | 24.184710 | Measurement + Hypo_Hyper | Test RMSE | 14.531746 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_CRNN_24.loc[tabla_1_CRNN_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_CRNN_24.loc[tabla_1_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_CRNN_24.loc[tabla_1_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_CRNN_24.loc[tabla_2_CRNN_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_CRNN_24.loc[tabla_2_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_CRNN_24.loc[tabla_2_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_CRNN_24.loc[tabla_3_CRNN_24['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_CRNN_24.loc[tabla_3_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_CRNN_24.loc[tabla_3_CRNN_24['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_24 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_24}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 13.840426 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 9.779933 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 14.453549 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 15.803531 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 10.193991 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 14.531746 | Measurement + Hypo_Hyper | 3 |
La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.
Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.
La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.
En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.
from IPython.display import display, HTML
html_tables_24 = f"""
<style>
.table-wrapper {{
display: inline-block;
font-size: 14px;
vertical-align: top;
margin-right: 20px;
}}
.table-wrapper table {{
font-size: 14px;
}}
.table-wrapper th, .table-wrapper td {{
width: 100px;
}}
.table-wrapper h3 {{
text-align: center;
font-size: 18px;
color: blue;
}}
h2 {{
text-align: center;
}}
</style>
<h2>Paciente 24</h2>
<div class="table-wrapper">
<h3>LSTM</h3>
{LSTM_24}
</div>
<div class="table-wrapper">
<h3>CRNN</h3>
{CRNN_24}
</div>
"""
display(HTML(html_tables_24))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.848579 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.794391 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.856952 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 5.513992 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 0.927484 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 0.975038 | Measurement + Hypo_Hyper | 2 |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 13.840426 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 9.779933 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 14.453549 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 15.803531 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 10.193991 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 14.531746 | Measurement + Hypo_Hyper | 3 |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_24': 1, 'tabla_2_CRNN_24': 2, 'tabla_3_CRNN_24': 3}
tables_arch2 = {'tabla_1_LSTM_24': 1, 'tabla_2_LSTM_24': 2, 'tabla_3_LSTM_24': 3}
# Para cada fila
for row in rows:
min_vals = []
# Arquitectura 1
for table_name, arch in tables_arch1.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'CRNN', arch))
# Arquitectura 2
for table_name, arch in tables_arch2.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'LSTM', arch))
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [min_arch],
'Arquitectura (Hiperparámetros)': [min_num]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 24)</span></center>"
display(HTML(html_text))
# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()
# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))
# Nombrando
styled_result2 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result2 = styled_result2.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_24.style.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"
# Muestra el HTML
display(HTML(html))
# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))
# Mostrar la figura
display(fig_hypo_hype_2_LSTM_24)
# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_24.style.set_properties(**{'text-align': 'center'}).hide(axis='index')
# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"
# Mostrar la tabla centrada
display(HTML(table_html))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.848579 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.794391 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.856952 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 5.513992 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 0.927484 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 0.975038 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-03-12 08:45:00 | nan | 156.134811 |
| 2022-03-12 09:00:00 | nan | 155.769943 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12836 | 100.00% |
| B | 0 | 0.00% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
Los resultados nos aclara que la mejor opción para el paciente 24 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.
Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):
La conclusión final para el paciente 24 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.
La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 11 debido a que tras un análisis extenso se ha comprobado que es el 2º que tiene más mediciones.
Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.
Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:
Arquitectura de hiperparámetros de LSTM:
Arquitectura de hiperparámetros de CRNN:
La característica de entrada va a ser: 'Measurement + Hypo_Hyper'
# Filtrar los datos para el paciente 11
df_paciente_11 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 11]
# Imprimir los resultados
df_paciente_11
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-06-12 20:15:00 | 11 | 2018-06-12 | 1900-01-01 20:18:00 | 178 | True | False | False | 20 | 1 |
| 2018-06-12 20:30:00 | 11 | 2018-06-12 | 1900-01-01 20:33:00 | 178 | True | False | False | 20 | 1 |
| 2018-06-12 20:45:00 | 11 | 2018-06-12 | 1900-01-01 20:48:00 | 166 | True | False | False | 20 | 1 |
| 2018-06-12 21:00:00 | 11 | 2018-06-12 | 1900-01-01 21:03:00 | 157 | True | False | False | 21 | 1 |
| 2018-06-12 21:15:00 | 11 | 2018-06-12 | 1900-01-01 21:17:00 | 131 | True | False | False | 21 | 1 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-02-25 10:30:00 | 11 | 2022-02-25 | 1900-01-01 10:30:00 | 174 | True | False | False | 10 | 4 |
| 2022-02-25 11:00:00 | 11 | 2022-02-25 | 1900-01-01 11:00:00 | 171 | True | False | False | 11 | 4 |
| 2022-02-25 11:15:00 | 11 | 2022-02-25 | 1900-01-01 11:15:00 | 167 | True | False | False | 11 | 4 |
| 2022-02-25 11:30:00 | 11 | 2022-02-25 | 1900-01-01 11:30:00 | 164 | True | False | False | 11 | 4 |
| 2022-02-25 19:15:00 | 11 | 2022-02-25 | 1900-01-01 19:15:00 | 178 | True | False | False | 19 | 4 |
11332 rows × 9 columns
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-06-12 20:18:00 | 178 | False | False |
| 2018-06-12 20:33:00 | 178 | False | False |
| 2018-06-12 20:48:00 | 166 | False | False |
| 2018-06-12 21:03:00 | 157 | False | False |
| 2018-06-12 21:17:00 | 131 | False | False |
| ... | ... | ... | ... |
| 2022-02-25 10:30:00 | 174 | False | False |
| 2022-02-25 11:00:00 | 171 | False | False |
| 2022-02-25 11:15:00 | 167 | False | False |
| 2022-02-25 11:30:00 | 164 | False | False |
| 2022-02-25 19:15:00 | 178 | False | False |
11332 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_27"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_34 (LSTM) (None, 64) 17408
dense_27 (Dense) (None, 1) 65
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1625/1625 [==============================] - 6s 3ms/step - loss: 3.7017 - val_loss: 1.8222 Epoch 2/20 1625/1625 [==============================] - 5s 3ms/step - loss: 2.4894 - val_loss: 1.0617 Epoch 3/20 1625/1625 [==============================] - 5s 3ms/step - loss: 2.7856 - val_loss: 1.9999 Epoch 4/20 1625/1625 [==============================] - 5s 3ms/step - loss: 2.4887 - val_loss: 0.6329 Epoch 5/20 1625/1625 [==============================] - 5s 3ms/step - loss: 2.3033 - val_loss: 2.0450 Epoch 6/20 1625/1625 [==============================] - 5s 3ms/step - loss: 2.0181 - val_loss: 0.3167 Epoch 7/20 1625/1625 [==============================] - 5s 3ms/step - loss: 1.8943 - val_loss: 1.3279 Epoch 8/20 1625/1625 [==============================] - 5s 3ms/step - loss: 1.7665 - val_loss: 1.5087 Epoch 9/20 1625/1625 [==============================] - 5s 3ms/step - loss: 1.7000 - val_loss: 0.2722 Epoch 10/20 1625/1625 [==============================] - 5s 3ms/step - loss: 1.7749 - val_loss: 0.3763 Epoch 11/20 1625/1625 [==============================] - 5s 3ms/step - loss: 1.5242 - val_loss: 0.3488 Epoch 12/20 1625/1625 [==============================] - 5s 3ms/step - loss: 1.5787 - val_loss: 0.8497
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_11 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_11 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_11 = test_eval
train_rmse_measurement_hypo_hype_2_LSTM_11 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_11 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_11 = test_rmse
3250/3250 [==============================] - 3s 829us/step 406/406 [==============================] - 0s 845us/step 406/406 [==============================] - 0s 862us/step Evaluación Entrenamiento MAE: 1.4609659910202026 Evaluación Validación MAE: 0.8496881127357483 Evaluación Prueba MAE: 0.8687956929206848 Entrenamiento RMSE: 4.643505096435547 Validación RMSE: 1.4289164543151855 Prueba RMSE: 1.4068015813827515
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
future_predictions_hypo_hype_2_LSTM_11 = future_predictions
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 9ms/step
Datos reales Datos predichos
date
2022-02-25 19:30:00 NaN 179.642593
2022-02-25 19:45:00 NaN 173.928604
def clarke_error_grid(ref_values, pred_values):
assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))
if max(ref_values) > 400 or max(pred_values) > 400:
print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
if min(ref_values) < 0 or min(pred_values) < 0:
print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values), min(pred_values)))
plt.clf()
plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
plt.title("Clarke Error Grid")
plt.xlabel("Reference Concentration (mg/dl)")
plt.ylabel("Prediction Concentration (mg/dl)")
plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.gca().set_facecolor('white')
plt.gca().set_xlim([0, 400])
plt.gca().set_ylim([0, 400])
plt.gca().set_aspect((400)/(400))
plt.plot([0, 400], [0, 400], ':', c='black')
plt.plot([0, 175/3], [70, 70], '-', c='black')
plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
plt.plot([70, 70], [84, 400],'-', c='black')
plt.plot([0, 70], [180, 180], '-', c='black')
plt.plot([70, 290], [180, 400],'-', c='black')
plt.plot([70, 70], [0, 56], '-', c='black')
plt.plot([70, 400], [56, 320],'-', c='black')
plt.plot([180, 180], [0, 70], '-', c='black')
plt.plot([180, 400], [70, 70], '-', c='black')
plt.plot([240, 240], [70, 180],'-', c='black')
plt.plot([240, 400], [180, 180], '-', c='black')
plt.plot([130, 180], [0, 70], '-', c='black')
plt.text(30, 15, "A", fontsize=15)
plt.text(370, 260, "B", fontsize=15)
plt.text(280, 370, "B", fontsize=15)
plt.text(160, 370, "C", fontsize=15)
plt.text(160, 15, "C", fontsize=15)
plt.text(30, 140, "D", fontsize=15)
plt.text(370, 120, "D", fontsize=15)
plt.text(30, 370, "E", fontsize=15)
plt.text(370, 15, "E", fontsize=15)
zone = [0] * 5
for i in range(len(ref_values)):
if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
zone[0] += 1 # Zone A
elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
zone[4] += 1 # Zone E
elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
zone[2] += 1 # Zone C
elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
zone[3] += 1 # Zone D
else:
zone[1] += 1 # Zone B
return plt, zone
ref_values = y_test
pred_values = y_pred_test
plt, zone = clarke_error_grid(ref_values, pred_values)
# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_11 = plt.gcf()
# Mostrar la figura
plt.show()
# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})
# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total
# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))
# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_11 = zone_df
# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_11.style.hide(axis="index"))
Conteo de Zonas:
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12989 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_11 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_11 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_11 = test_eval
train_rmse_measurement_hypo_hype_1_LSTM_11 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_11 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_11 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_54"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_64 (LSTM) (None, 32) 4608
dense_54 (Dense) (None, 1) 33
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1625/1625 [==============================] - 4s 2ms/step - loss: 122.8506 - val_loss: 118.6820
Epoch 2/20
1625/1625 [==============================] - 3s 2ms/step - loss: 86.1190 - val_loss: 82.7876
Epoch 3/20
1625/1625 [==============================] - 3s 2ms/step - loss: 52.5447 - val_loss: 49.1386
Epoch 4/20
1625/1625 [==============================] - 3s 2ms/step - loss: 36.4419 - val_loss: 24.5215
Epoch 5/20
1625/1625 [==============================] - 3s 2ms/step - loss: 25.8021 - val_loss: 16.5972
Epoch 6/20
1625/1625 [==============================] - 3s 2ms/step - loss: 13.1940 - val_loss: 10.0398
Epoch 7/20
1625/1625 [==============================] - 3s 2ms/step - loss: 7.8960 - val_loss: 6.0100
Epoch 8/20
1625/1625 [==============================] - 3s 2ms/step - loss: 5.3219 - val_loss: 2.3499
Epoch 9/20
1625/1625 [==============================] - 3s 2ms/step - loss: 3.9468 - val_loss: 1.6772
Epoch 10/20
1625/1625 [==============================] - 3s 2ms/step - loss: 3.3093 - val_loss: 0.9987
Epoch 11/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.7988 - val_loss: 1.5336
Epoch 12/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.5711 - val_loss: 0.5686
Epoch 13/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.3924 - val_loss: 0.4269
Epoch 14/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.3650 - val_loss: 0.4643
Epoch 15/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.2302 - val_loss: 0.3410
Epoch 16/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.2107 - val_loss: 1.1281
Epoch 17/20
1625/1625 [==============================] - 3s 2ms/step - loss: 2.1175 - val_loss: 0.6529
Epoch 18/20
1625/1625 [==============================] - 3s 2ms/step - loss: 1.9106 - val_loss: 1.3566
3250/3250 [==============================] - 2s 655us/step
406/406 [==============================] - 0s 679us/step
406/406 [==============================] - 0s 644us/step
Evaluación Entrenamiento MAE: 2.225130796432495
Evaluación Validación MAE: 1.356641411781311
Evaluación Prueba MAE: 1.5138938426971436
Entrenamiento RMSE: 5.859622478485107
Validación RMSE: 1.78261137008667
Prueba RMSE: 1.869420051574707
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_11 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_11 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_11 = test_eval
train_rmse_measurement_hypo_hype_3_LSTM_11 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_11 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_11 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_29"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_36 (LSTM) (None, 8, 32) 4608
batch_normalization_18 (Bat (None, 8, 32) 128
chNormalization)
dropout_18 (Dropout) (None, 8, 32) 0
lstm_37 (LSTM) (None, 64) 24832
batch_normalization_19 (Bat (None, 64) 256
chNormalization)
dropout_19 (Dropout) (None, 64) 0
dense_29 (Dense) (None, 1) 65
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1625/1625 [==============================] - 9s 5ms/step - loss: 40.8846 - val_loss: 11.6269
Epoch 2/20
1625/1625 [==============================] - 8s 5ms/step - loss: 26.0059 - val_loss: 13.8111
Epoch 3/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.5797 - val_loss: 11.0185
Epoch 4/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.3500 - val_loss: 16.4315
Epoch 5/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.1992 - val_loss: 12.4309
Epoch 6/20
1625/1625 [==============================] - 8s 5ms/step - loss: 25.0538 - val_loss: 10.6724
Epoch 7/20
1625/1625 [==============================] - 8s 5ms/step - loss: 24.8869 - val_loss: 12.2761
Epoch 8/20
1625/1625 [==============================] - 8s 5ms/step - loss: 24.8269 - val_loss: 14.5116
Epoch 9/20
1625/1625 [==============================] - 8s 5ms/step - loss: 24.7012 - val_loss: 12.3753
3250/3250 [==============================] - 4s 1ms/step
406/406 [==============================] - 1s 1ms/step
406/406 [==============================] - 1s 1ms/step
Evaluación Entrenamiento MAE: 14.121617317199707
Evaluación Validación MAE: 12.37533950805664
Evaluación Prueba MAE: 13.596681594848633
Entrenamiento RMSE: 17.937429428100586
Validación RMSE: 12.754348754882812
Prueba RMSE: 13.90673542022705
Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas LSTM utilizadas son:
Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_LSTM_11 vacía
tabla_1_LSTM_11 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_LSTM_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_LSTM_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_LSTM_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_11
tabla_1_LSTM_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_11
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 2.225131 |
| Validación MAE | 1.356641 |
| Test MAE | 1.513894 |
| Entrenamiento RMSE | 5.859622 |
| Validación RMSE | 1.782611 |
| Test RMSE | 1.869420 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_LSTM_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_LSTM_11 = pd.concat([tabla_1_LSTM_11, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla vacía
tabla_2_LSTM_11 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_LSTM_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_LSTM_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_LSTM_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_11
tabla_2_LSTM_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_11
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 1.460966 |
| Validación MAE | 0.849688 |
| Test MAE | 0.868796 |
| Entrenamiento RMSE | 4.643505 |
| Validación RMSE | 1.428916 |
| Test RMSE | 1.406802 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_LSTM_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_LSTM_11 = pd.concat([tabla_2_LSTM_11, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_LSTM_11 vacía
tabla_3_LSTM_11 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_LSTM_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_LSTM_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_LSTM_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_11
tabla_3_LSTM_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_11
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 14.121617 |
| Validación MAE | 12.375340 |
| Test MAE | 13.596682 |
| Entrenamiento RMSE | 17.937429 |
| Validación RMSE | 12.754349 |
| Test RMSE | 13.906735 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_LSTM_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_LSTM_11 = pd.concat([tabla_3_LSTM_11, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_11, tabla_2_LSTM_11, tabla_3_LSTM_11], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 2.225131 | Measurement + Hypo_Hyper | Entrenamiento MAE | 1.460966 | Measurement + Hypo_Hyper | Entrenamiento MAE | 14.121617 | Measurement + Hypo_Hyper |
| Validación MAE | 1.356641 | Measurement + Hypo_Hyper | Validación MAE | 0.849688 | Measurement + Hypo_Hyper | Validación MAE | 12.375340 | Measurement + Hypo_Hyper |
| Test MAE | 1.513894 | Measurement + Hypo_Hyper | Test MAE | 0.868796 | Measurement + Hypo_Hyper | Test MAE | 13.596682 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 5.859622 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 4.643505 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 17.937429 | Measurement + Hypo_Hyper |
| Validación RMSE | 1.782611 | Measurement + Hypo_Hyper | Validación RMSE | 1.428916 | Measurement + Hypo_Hyper | Validación RMSE | 12.754349 | Measurement + Hypo_Hyper |
| Test RMSE | 1.869420 | Measurement + Hypo_Hyper | Test RMSE | 1.406802 | Measurement + Hypo_Hyper | Test RMSE | 13.906735 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_LSTM_11.loc[tabla_1_LSTM_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_LSTM_11.loc[tabla_1_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_LSTM_11.loc[tabla_1_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_LSTM_11.loc[tabla_2_LSTM_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_LSTM_11.loc[tabla_2_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_LSTM_11.loc[tabla_2_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_LSTM_11.loc[tabla_3_LSTM_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_LSTM_11.loc[tabla_3_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_LSTM_11.loc[tabla_3_LSTM_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_11 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_11}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.460966 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.849688 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.868796 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 4.643505 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 1.428916 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.406802 | Measurement + Hypo_Hyper | 2 |
La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.
La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:
El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.
En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con LSTM con las características de entrada Measurement + Hypo_Hyper.
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-06-12 20:18:00 | 178 | False | False |
| 2018-06-12 20:33:00 | 178 | False | False |
| 2018-06-12 20:48:00 | 166 | False | False |
| 2018-06-12 21:03:00 | 157 | False | False |
| 2018-06-12 21:17:00 | 131 | False | False |
| ... | ... | ... | ... |
| 2022-02-25 10:30:00 | 174 | False | False |
| 2022-02-25 11:00:00 | 171 | False | False |
| 2022-02-25 11:15:00 | 167 | False | False |
| 2022-02-25 11:30:00 | 164 | False | False |
| 2022-02-25 19:15:00 | 178 | False | False |
11332 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_30"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv1d_6 (Conv1D) (None, 6, 32) 320
batch_normalization_20 (Bat (None, 6, 32) 128
chNormalization)
max_pooling1d_6 (MaxPooling (None, 3, 32) 0
1D)
dropout_20 (Dropout) (None, 3, 32) 0
lstm_38 (LSTM) (None, 64) 24832
batch_normalization_21 (Bat (None, 64) 256
chNormalization)
dropout_21 (Dropout) (None, 64) 0
dense_30 (Dense) (None, 1) 65
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1625/1625 [==============================] - 5s 2ms/step - loss: 41.7783 - val_loss: 18.1101 Epoch 2/20 1625/1625 [==============================] - 4s 2ms/step - loss: 24.9494 - val_loss: 12.0912 Epoch 3/20 1625/1625 [==============================] - 4s 2ms/step - loss: 24.6390 - val_loss: 19.6754 Epoch 4/20 1625/1625 [==============================] - 4s 2ms/step - loss: 24.6374 - val_loss: 22.1808 Epoch 5/20 1625/1625 [==============================] - 4s 2ms/step - loss: 24.6893 - val_loss: 3.5177 Epoch 6/20 1625/1625 [==============================] - 4s 2ms/step - loss: 24.6268 - val_loss: 7.0594 Epoch 7/20 1625/1625 [==============================] - 4s 2ms/step - loss: 24.6278 - val_loss: 11.3842 Epoch 8/20 1625/1625 [==============================] - 4s 2ms/step - loss: 24.5211 - val_loss: 7.6010
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_11 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_11 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_11 = test_eval
train_rmse_measurement_hypo_hype_3_CRNN_11 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_11 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_11 = test_rmse
3250/3250 [==============================] - 2s 703us/step 406/406 [==============================] - 0s 688us/step 406/406 [==============================] - 0s 693us/step Evaluación Entrenamiento MAE: 13.247295379638672 Evaluación Validación MAE: 7.600970268249512 Evaluación Prueba MAE: 7.071486473083496 Entrenamiento RMSE: 17.684404373168945 Validación RMSE: 8.325462341308594 Prueba RMSE: 7.5740556716918945
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 11ms/step
Datos reales Datos predichos
date
2022-02-25 19:30:00 NaN 168.789627
2022-02-25 19:45:00 NaN 168.962662
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_2_CRNN_11 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_11 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_11 = test_eval
train_rmse_measurement_hypo_hype_2_CRNN_11 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_11 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_11 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1625/1625 [==============================] - 3s 2ms/step - loss: 119.8955 - val_loss: 112.7883 Epoch 2/20 1625/1625 [==============================] - 2s 1ms/step - loss: 75.4090 - val_loss: 67.8727 Epoch 3/20 1625/1625 [==============================] - 2s 1ms/step - loss: 51.0689 - val_loss: 45.4892 Epoch 4/20 1625/1625 [==============================] - 2s 1ms/step - loss: 50.7536 - val_loss: 48.2311 Epoch 5/20 1625/1625 [==============================] - 2s 1ms/step - loss: 57.1844 - val_loss: 59.5624 Epoch 6/20 1625/1625 [==============================] - 2s 1ms/step - loss: 58.2487 - val_loss: 61.5965 3250/3250 [==============================] - 2s 545us/step 406/406 [==============================] - 0s 544us/step 406/406 [==============================] - 0s 553us/step Evaluación Entrenamiento MAE: 49.19638442993164 Evaluación Validación MAE: 61.59640884399414 Evaluación Prueba MAE: 65.90991973876953 Entrenamiento RMSE: 54.69769287109375 Validación RMSE: 62.3424186706543 Prueba RMSE: 66.49855041503906
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_11[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_1_CRNN_11 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_11 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_11 = test_eval
train_rmse_measurement_hypo_hype_1_CRNN_11 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_11 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_11 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1625/1625 [==============================] - 3s 1ms/step - loss: 125.1699 - val_loss: 121.2117 Epoch 2/20 1625/1625 [==============================] - 2s 1ms/step - loss: 87.0778 - val_loss: 82.7230 Epoch 3/20 1625/1625 [==============================] - 2s 1ms/step - loss: 52.2566 - val_loss: 48.1539 Epoch 4/20 1625/1625 [==============================] - 2s 1ms/step - loss: 53.7223 - val_loss: 52.8987 Epoch 5/20 1625/1625 [==============================] - 2s 1ms/step - loss: 55.2015 - val_loss: 57.0709 Epoch 6/20 1625/1625 [==============================] - 2s 1ms/step - loss: 51.4649 - val_loss: 47.6335 Epoch 7/20 1625/1625 [==============================] - 2s 1ms/step - loss: 50.0839 - val_loss: 51.8648 Epoch 8/20 1625/1625 [==============================] - 2s 1ms/step - loss: 52.8192 - val_loss: 55.3951 Epoch 9/20 1625/1625 [==============================] - 2s 1ms/step - loss: 52.1573 - val_loss: 51.6825 3250/3250 [==============================] - 2s 507us/step 406/406 [==============================] - 0s 501us/step 406/406 [==============================] - 0s 514us/step Evaluación Entrenamiento MAE: 40.94864273071289 Evaluación Validación MAE: 51.68236541748047 Evaluación Prueba MAE: 55.995872497558594 Entrenamiento RMSE: 46.460914611816406 Validación RMSE: 52.56913375854492 Prueba RMSE: 56.68741226196289
Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas CRNN utilizadas son:
Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_11 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_CRNN_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_CRNN_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_CRNN_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_11
tabla_1_CRNN_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_11
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 40.948643 |
| Validación MAE | 51.682365 |
| Test MAE | 55.995872 |
| Entrenamiento RMSE | 46.460915 |
| Validación RMSE | 52.569134 |
| Test RMSE | 56.687412 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_CRNN_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_CRNN_11 = pd.concat([tabla_1_CRNN_11, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_11 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_CRNN_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_CRNN_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_CRNN_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_11
tabla_2_CRNN_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_11
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 49.196384 |
| Validación MAE | 61.596409 |
| Test MAE | 65.909920 |
| Entrenamiento RMSE | 54.697693 |
| Validación RMSE | 62.342419 |
| Test RMSE | 66.498550 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_CRNN_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_CRNN_11 = pd.concat([tabla_2_CRNN_11, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_11 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_CRNN_11.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_11.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_11.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_CRNN_11.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_11.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_11.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_CRNN_11.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_11
tabla_3_CRNN_11.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_11
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_11.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 13.247295 |
| Validación MAE | 7.600970 |
| Test MAE | 7.071486 |
| Entrenamiento RMSE | 17.684404 |
| Validación RMSE | 8.325462 |
| Test RMSE | 7.574056 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_11.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_CRNN_11 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_CRNN_11 = pd.concat([tabla_3_CRNN_11, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_11, tabla_2_CRNN_11, tabla_3_CRNN_11], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 40.948643 | Measurement + Hypo_Hyper | Entrenamiento MAE | 49.196384 | Measurement + Hypo_Hyper | Entrenamiento MAE | 13.247295 | Measurement + Hypo_Hyper |
| Validación MAE | 51.682365 | Measurement + Hypo_Hyper | Validación MAE | 61.596409 | Measurement + Hypo_Hyper | Validación MAE | 7.600970 | Measurement + Hypo_Hyper |
| Test MAE | 55.995872 | Measurement + Hypo_Hyper | Test MAE | 65.909920 | Measurement + Hypo_Hyper | Test MAE | 7.071486 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 46.460915 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 54.697693 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 17.684404 | Measurement + Hypo_Hyper |
| Validación RMSE | 52.569134 | Measurement + Hypo_Hyper | Validación RMSE | 62.342419 | Measurement + Hypo_Hyper | Validación RMSE | 8.325462 | Measurement + Hypo_Hyper |
| Test RMSE | 56.687412 | Measurement + Hypo_Hyper | Test RMSE | 66.498550 | Measurement + Hypo_Hyper | Test RMSE | 7.574056 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_CRNN_11.loc[tabla_1_CRNN_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_CRNN_11.loc[tabla_1_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_CRNN_11.loc[tabla_1_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_CRNN_11.loc[tabla_2_CRNN_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_CRNN_11.loc[tabla_2_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_CRNN_11.loc[tabla_2_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_CRNN.loc[tabla_3_CRNN_11['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_CRNN.loc[tabla_3_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_CRNN.loc[tabla_3_CRNN_11['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_11 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_11}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 15.537673 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 15.351344 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 14.596274 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 19.507032 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 16.051613 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 16.385645 | Measurement + Hypo_Hyper | 3 |
La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.
Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.
La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.
En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 3 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.
from IPython.display import display, HTML
html_tables_11 = f"""
<style>
.table-wrapper {{
display: inline-block;
font-size: 14px;
vertical-align: top;
margin-right: 20px;
}}
.table-wrapper table {{
font-size: 14px;
}}
.table-wrapper th, .table-wrapper td {{
width: 100px;
}}
.table-wrapper h3 {{
text-align: center;
font-size: 18px;
color: blue;
}}
h2 {{
text-align: center;
}}
</style>
<h2>Paciente 11</h2>
<div class="table-wrapper">
<h3>LSTM</h3>
{LSTM_11}
</div>
<div class="table-wrapper">
<h3>CRNN</h3>
{CRNN_11}
</div>
"""
display(HTML(html_tables_11))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.460966 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.849688 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.868796 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 4.643505 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 1.428916 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.406802 | Measurement + Hypo_Hyper | 2 |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 15.537673 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 15.351344 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 14.596274 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 19.507032 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 16.051613 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 16.385645 | Measurement + Hypo_Hyper | 3 |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_11': 1, 'tabla_2_CRNN_11': 2, 'tabla_3_CRNN_11': 3}
tables_arch2 = {'tabla_1_LSTM_11': 1, 'tabla_2_LSTM_11': 2, 'tabla_3_LSTM_11': 3}
# Para cada fila
for row in rows:
min_vals = []
# Arquitectura 1
for table_name, arch in tables_arch1.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'CRNN', arch))
# Arquitectura 2
for table_name, arch in tables_arch2.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'LSTM', arch))
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [min_arch],
'Arquitectura (Hiperparámetros)': [min_num]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 11)</span></center>"
display(HTML(html_text))
# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()
# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))
# Nombrando
styled_result3 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result3 = styled_result3.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_11.style.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"
# Muestra el HTML
display(HTML(html))
# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))
# Mostrar la figura
display(fig_hypo_hype_2_LSTM_11)
# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_11.style.set_properties(**{'text-align': 'center'}).hide(axis='index')
# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"
# Mostrar la tabla centrada
display(HTML(table_html))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.460966 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.849688 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.868796 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 4.643505 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 1.428916 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.406802 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-02-25 19:30:00 | nan | 179.642593 |
| 2022-02-25 19:45:00 | nan | 173.928604 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12989 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
Los resultados nos aclara que la mejor opción para el paciente 11 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.
Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):
La conclusión final para el paciente 11 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.
La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 83 debido a que tras un análisis extenso se ha comprobado que es el 4º con mayor número de mediciones.
Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.
Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:
Arquitectura de hiperparámetros de LSTM:
Arquitectura de hiperparámetros de CRNN:
La característica de entrada va a ser: 'Measurement + Hypo_Hyper'
# Filtrar los datos para el paciente 83
df_paciente_83 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 83]
# Imprimir los resultados
df_paciente_83
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-02-21 19:45:00 | 83 | 2018-02-21 | 1900-01-01 19:46:00 | 121 | True | False | False | 19 | 2 |
| 2018-02-21 20:00:00 | 83 | 2018-02-21 | 1900-01-01 20:01:00 | 124 | True | False | False | 20 | 2 |
| 2018-02-21 20:15:00 | 83 | 2018-02-21 | 1900-01-01 20:17:00 | 131 | True | False | False | 20 | 2 |
| 2018-02-21 20:30:00 | 83 | 2018-02-21 | 1900-01-01 20:32:00 | 128 | True | False | False | 20 | 2 |
| 2018-02-21 20:45:00 | 83 | 2018-02-21 | 1900-01-01 20:47:00 | 129 | True | False | False | 20 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-03-14 06:45:00 | 83 | 2022-03-14 | 1900-01-01 06:45:00 | 159 | True | False | False | 6 | 0 |
| 2022-03-14 07:00:00 | 83 | 2022-03-14 | 1900-01-01 07:00:00 | 160 | True | False | False | 7 | 0 |
| 2022-03-14 07:15:00 | 83 | 2022-03-14 | 1900-01-01 07:15:00 | 164 | True | False | False | 7 | 0 |
| 2022-03-14 10:00:00 | 83 | 2022-03-14 | 1900-01-01 10:00:00 | 182 | True | False | False | 10 | 0 |
| 2022-03-14 10:45:00 | 83 | 2022-03-14 | 1900-01-01 10:45:00 | 168 | True | False | False | 10 | 0 |
9198 rows × 9 columns
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-02-21 19:46:00 | 121 | False | False |
| 2018-02-21 20:01:00 | 124 | False | False |
| 2018-02-21 20:17:00 | 131 | False | False |
| 2018-02-21 20:32:00 | 128 | False | False |
| 2018-02-21 20:47:00 | 129 | False | False |
| ... | ... | ... | ... |
| 2022-03-14 06:45:00 | 159 | False | False |
| 2022-03-14 07:00:00 | 160 | False | False |
| 2022-03-14 07:15:00 | 164 | False | False |
| 2022-03-14 10:00:00 | 182 | False | False |
| 2022-03-14 10:45:00 | 168 | False | False |
9198 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_33"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_41 (LSTM) (None, 64) 17408
dense_33 (Dense) (None, 1) 65
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1778/1778 [==============================] - 5s 2ms/step - loss: 2.9965 - val_loss: 1.8547 Epoch 2/20 1778/1778 [==============================] - 4s 2ms/step - loss: 2.0011 - val_loss: 0.8204 Epoch 3/20 1778/1778 [==============================] - 4s 2ms/step - loss: 2.0433 - val_loss: 1.8748 Epoch 4/20 1778/1778 [==============================] - 4s 2ms/step - loss: 1.7310 - val_loss: 0.6916 Epoch 5/20 1778/1778 [==============================] - 4s 2ms/step - loss: 1.7213 - val_loss: 2.5189 Epoch 6/20 1778/1778 [==============================] - 4s 2ms/step - loss: 1.5553 - val_loss: 0.6286 Epoch 7/20 1778/1778 [==============================] - 4s 2ms/step - loss: 1.3384 - val_loss: 0.5956 Epoch 8/20 1778/1778 [==============================] - 4s 2ms/step - loss: 1.5467 - val_loss: 0.8822 Epoch 9/20 1778/1778 [==============================] - 4s 2ms/step - loss: 1.3162 - val_loss: 1.3294 Epoch 10/20 1778/1778 [==============================] - 4s 2ms/step - loss: 1.2281 - val_loss: 0.6102
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_83 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_83 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_83 = test_eval
train_rmse_measurement_hypo_hype_2_LSTM_83 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_83 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_83 = test_rmse
3556/3556 [==============================] - 3s 787us/step 445/445 [==============================] - 0s 788us/step 445/445 [==============================] - 0s 791us/step Evaluación Entrenamiento MAE: 0.8629071712493896 Evaluación Validación MAE: 0.6101782321929932 Evaluación Prueba MAE: 0.465580016374588 Entrenamiento RMSE: 3.4258315563201904 Validación RMSE: 1.7380841970443726 Prueba RMSE: 1.2392966747283936
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
# Almacena dato
future_predictions_hypo_hype_2_LSTM_83 = future_predictions
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 9ms/step
Datos reales Datos predichos
date
2022-03-14 11:00:00 NaN 166.793915
2022-03-14 11:15:00 NaN 165.903671
def clarke_error_grid(ref_values, pred_values):
assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))
if max(ref_values) > 400 or max(pred_values) > 400:
print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
if min(ref_values) < 0 or min(pred_values) < 0:
print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values), min(pred_values)))
plt.clf()
plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
plt.title("Clarke Error Grid")
plt.xlabel("Reference Concentration (mg/dl)")
plt.ylabel("Prediction Concentration (mg/dl)")
plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.gca().set_facecolor('white')
plt.gca().set_xlim([0, 400])
plt.gca().set_ylim([0, 400])
plt.gca().set_aspect((400)/(400))
plt.plot([0, 400], [0, 400], ':', c='black')
plt.plot([0, 175/3], [70, 70], '-', c='black')
plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
plt.plot([70, 70], [84, 400],'-', c='black')
plt.plot([0, 70], [180, 180], '-', c='black')
plt.plot([70, 290], [180, 400],'-', c='black')
plt.plot([70, 70], [0, 56], '-', c='black')
plt.plot([70, 400], [56, 320],'-', c='black')
plt.plot([180, 180], [0, 70], '-', c='black')
plt.plot([180, 400], [70, 70], '-', c='black')
plt.plot([240, 240], [70, 180],'-', c='black')
plt.plot([240, 400], [180, 180], '-', c='black')
plt.plot([130, 180], [0, 70], '-', c='black')
plt.text(30, 15, "A", fontsize=15)
plt.text(370, 260, "B", fontsize=15)
plt.text(280, 370, "B", fontsize=15)
plt.text(160, 370, "C", fontsize=15)
plt.text(160, 15, "C", fontsize=15)
plt.text(30, 140, "D", fontsize=15)
plt.text(370, 120, "D", fontsize=15)
plt.text(30, 370, "E", fontsize=15)
plt.text(370, 15, "E", fontsize=15)
zone = [0] * 5
for i in range(len(ref_values)):
if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
zone[0] += 1 # Zone A
elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
zone[4] += 1 # Zone E
elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
zone[2] += 1 # Zone C
elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
zone[3] += 1 # Zone D
else:
zone[1] += 1 # Zone B
return plt, zone
ref_values = y_test
pred_values = y_pred_test
plt, zone = clarke_error_grid(ref_values, pred_values)
# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_83 = plt.gcf()
# Mostrar la figura
plt.show()
# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})
# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total
# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))
# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_83 = zone_df
# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_83.style.hide(axis="index"))
Conteo de Zonas:
| Zona | Conteo | Proporción |
|---|---|---|
| A | 14216 | 100.00% |
| B | 0 | 0.00% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_83 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_83 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_83 = test_eval
train_rmse_measurement_hypo_hype_1_LSTM_83 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_83 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_83 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_34"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_42 (LSTM) (None, 32) 4608
dense_34 (Dense) (None, 1) 33
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1778/1778 [==============================] - 4s 2ms/step - loss: 123.7073 - val_loss: 113.1900
Epoch 2/20
1778/1778 [==============================] - 3s 2ms/step - loss: 83.3717 - val_loss: 73.8789
Epoch 3/20
1778/1778 [==============================] - 3s 2ms/step - loss: 45.5802 - val_loss: 33.9464
Epoch 4/20
1778/1778 [==============================] - 3s 2ms/step - loss: 44.3730 - val_loss: 34.0456
Epoch 5/20
1778/1778 [==============================] - 3s 2ms/step - loss: 40.5904 - val_loss: 21.5790
Epoch 6/20
1778/1778 [==============================] - 3s 2ms/step - loss: 34.1987 - val_loss: 21.2392
Epoch 7/20
1778/1778 [==============================] - 3s 2ms/step - loss: 28.9748 - val_loss: 15.3343
Epoch 8/20
1778/1778 [==============================] - 3s 2ms/step - loss: 38.4588 - val_loss: 26.2944
Epoch 9/20
1778/1778 [==============================] - 3s 2ms/step - loss: 35.9527 - val_loss: 27.6839
Epoch 10/20
1778/1778 [==============================] - 3s 2ms/step - loss: 37.1429 - val_loss: 27.5536
3556/3556 [==============================] - 2s 618us/step
445/445 [==============================] - 0s 624us/step
445/445 [==============================] - 0s 621us/step
Evaluación Entrenamiento MAE: 25.64620590209961
Evaluación Validación MAE: 27.55359649658203
Evaluación Prueba MAE: 36.11166000366211
Entrenamiento RMSE: 30.676008224487305
Validación RMSE: 29.090604782104492
Prueba RMSE: 37.675025939941406
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_83 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_83 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_83 = test_eval
train_rmse_measurement_hypo_hype_3_LSTM_83 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_83 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_83 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_35"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_43 (LSTM) (None, 8, 32) 4608
batch_normalization_22 (Bat (None, 8, 32) 128
chNormalization)
dropout_22 (Dropout) (None, 8, 32) 0
lstm_44 (LSTM) (None, 64) 24832
batch_normalization_23 (Bat (None, 64) 256
chNormalization)
dropout_23 (Dropout) (None, 64) 0
dense_35 (Dense) (None, 1) 65
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1778/1778 [==============================] - 9s 4ms/step - loss: 35.7242 - val_loss: 3.0762
Epoch 2/20
1778/1778 [==============================] - 8s 4ms/step - loss: 20.6317 - val_loss: 7.3408
Epoch 3/20
1778/1778 [==============================] - 7s 4ms/step - loss: 19.7760 - val_loss: 8.0503
Epoch 4/20
1778/1778 [==============================] - 7s 4ms/step - loss: 20.0242 - val_loss: 5.9338
3556/3556 [==============================] - 4s 1ms/step
445/445 [==============================] - 1s 1ms/step
445/445 [==============================] - 1s 1ms/step
Evaluación Entrenamiento MAE: 8.0759916305542
Evaluación Validación MAE: 5.93383264541626
Evaluación Prueba MAE: 8.28266716003418
Entrenamiento RMSE: 11.11864185333252
Validación RMSE: 6.500219345092773
Prueba RMSE: 8.75619125366211
Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas LSTM utilizadas son:
Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_LSTM_83 vacía
tabla_1_LSTM_83 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_LSTM_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_LSTM_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_LSTM_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_83
tabla_1_LSTM_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_83
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 25.646206 |
| Validación MAE | 27.553596 |
| Test MAE | 36.111660 |
| Entrenamiento RMSE | 30.676008 |
| Validación RMSE | 29.090605 |
| Test RMSE | 37.675026 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_LSTM_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_LSTM_83 = pd.concat([tabla_1_LSTM_83, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla vacía
tabla_2_LSTM_83 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_LSTM_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_LSTM_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_LSTM_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_83
tabla_2_LSTM_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_83
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 0.862907 |
| Validación MAE | 0.610178 |
| Test MAE | 0.465580 |
| Entrenamiento RMSE | 3.425832 |
| Validación RMSE | 1.738084 |
| Test RMSE | 1.239297 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_LSTM_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_LSTM_83 = pd.concat([tabla_2_LSTM_83, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_LSTM_83 vacía
tabla_3_LSTM_83 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_LSTM_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_LSTM_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_LSTM_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_83
tabla_3_LSTM_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_83
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 8.075992 |
| Validación MAE | 5.933833 |
| Test MAE | 8.282667 |
| Entrenamiento RMSE | 11.118642 |
| Validación RMSE | 6.500219 |
| Test RMSE | 8.756191 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_LSTM_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_LSTM_83 = pd.concat([tabla_3_LSTM_83, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_83, tabla_2_LSTM_83, tabla_3_LSTM_83], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 25.646206 | Measurement + Hypo_Hyper | Entrenamiento MAE | 0.862907 | Measurement + Hypo_Hyper | Entrenamiento MAE | 8.075992 | Measurement + Hypo_Hyper |
| Validación MAE | 27.553596 | Measurement + Hypo_Hyper | Validación MAE | 0.610178 | Measurement + Hypo_Hyper | Validación MAE | 5.933833 | Measurement + Hypo_Hyper |
| Test MAE | 36.111660 | Measurement + Hypo_Hyper | Test MAE | 0.465580 | Measurement + Hypo_Hyper | Test MAE | 8.282667 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 30.676008 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 3.425832 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 11.118642 | Measurement + Hypo_Hyper |
| Validación RMSE | 29.090605 | Measurement + Hypo_Hyper | Validación RMSE | 1.738084 | Measurement + Hypo_Hyper | Validación RMSE | 6.500219 | Measurement + Hypo_Hyper |
| Test RMSE | 37.675026 | Measurement + Hypo_Hyper | Test RMSE | 1.239297 | Measurement + Hypo_Hyper | Test RMSE | 8.756191 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_LSTM_83.loc[tabla_1_LSTM_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_LSTM_83.loc[tabla_1_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_LSTM_83.loc[tabla_1_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_LSTM_83.loc[tabla_2_LSTM_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_LSTM_83.loc[tabla_2_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_LSTM_83.loc[tabla_2_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_LSTM_83.loc[tabla_3_LSTM_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_LSTM_83.loc[tabla_3_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_LSTM_83.loc[tabla_3_LSTM_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_83 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_83}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 0.862907 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.610178 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.465580 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 3.425832 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 1.738084 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.239297 | Measurement + Hypo_Hyper | 2 |
La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.
La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:
El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.
En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-02-21 19:46:00 | 121 | False | False |
| 2018-02-21 20:01:00 | 124 | False | False |
| 2018-02-21 20:17:00 | 131 | False | False |
| 2018-02-21 20:32:00 | 128 | False | False |
| 2018-02-21 20:47:00 | 129 | False | False |
| ... | ... | ... | ... |
| 2022-03-14 06:45:00 | 159 | False | False |
| 2022-03-14 07:00:00 | 160 | False | False |
| 2022-03-14 07:15:00 | 164 | False | False |
| 2022-03-14 10:00:00 | 182 | False | False |
| 2022-03-14 10:45:00 | 168 | False | False |
9198 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_36"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv1d_9 (Conv1D) (None, 6, 32) 320
batch_normalization_24 (Bat (None, 6, 32) 128
chNormalization)
max_pooling1d_9 (MaxPooling (None, 3, 32) 0
1D)
dropout_24 (Dropout) (None, 3, 32) 0
lstm_45 (LSTM) (None, 64) 24832
batch_normalization_25 (Bat (None, 64) 256
chNormalization)
dropout_25 (Dropout) (None, 64) 0
dense_36 (Dense) (None, 1) 65
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1778/1778 [==============================] - 5s 2ms/step - loss: 33.9017 - val_loss: 12.1354 Epoch 2/20 1778/1778 [==============================] - 4s 2ms/step - loss: 19.9794 - val_loss: 19.2859 Epoch 3/20 1778/1778 [==============================] - 4s 2ms/step - loss: 19.7206 - val_loss: 4.7905 Epoch 4/20 1778/1778 [==============================] - 4s 2ms/step - loss: 19.6063 - val_loss: 7.1944 Epoch 5/20 1778/1778 [==============================] - 4s 2ms/step - loss: 19.4487 - val_loss: 13.1217 Epoch 6/20 1778/1778 [==============================] - 4s 2ms/step - loss: 19.3297 - val_loss: 3.5562 Epoch 7/20 1778/1778 [==============================] - 4s 2ms/step - loss: 19.1056 - val_loss: 9.4604 Epoch 8/20 1778/1778 [==============================] - 4s 2ms/step - loss: 19.0466 - val_loss: 19.4459 Epoch 9/20 1778/1778 [==============================] - 4s 2ms/step - loss: 18.8525 - val_loss: 9.1188
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_83 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_83 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_83 = test_eval
train_rmse_measurement_hypo_hype_3_CRNN_83 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_83 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_83 = test_rmse
3556/3556 [==============================] - 2s 650us/step 445/445 [==============================] - 0s 654us/step 445/445 [==============================] - 0s 667us/step Evaluación Entrenamiento MAE: 11.620800971984863 Evaluación Validación MAE: 9.118827819824219 Evaluación Prueba MAE: 12.937151908874512 Entrenamiento RMSE: 18.15708351135254 Validación RMSE: 10.958942413330078 Prueba RMSE: 14.02910327911377
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-03-14 11:00:00 NaN 188.401703
2022-03-14 11:15:00 NaN 192.112320
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_2_CRNN_83 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_83 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_83 = test_eval
train_rmse_measurement_hypo_hype_2_CRNN_83 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_83 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_83 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1778/1778 [==============================] - 4s 2ms/step - loss: 121.2205 - val_loss: 108.6773 Epoch 2/20 1778/1778 [==============================] - 3s 1ms/step - loss: 76.5391 - val_loss: 64.7054 Epoch 3/20 1778/1778 [==============================] - 3s 2ms/step - loss: 56.9476 - val_loss: 49.4607 Epoch 4/20 1778/1778 [==============================] - 3s 2ms/step - loss: 52.9150 - val_loss: 48.6064 Epoch 5/20 1778/1778 [==============================] - 3s 2ms/step - loss: 55.8967 - val_loss: 55.9629 Epoch 6/20 1778/1778 [==============================] - 3s 2ms/step - loss: 56.1408 - val_loss: 57.0564 Epoch 7/20 1778/1778 [==============================] - 3s 2ms/step - loss: 58.9478 - val_loss: 62.0105 3556/3556 [==============================] - 2s 564us/step 445/445 [==============================] - 0s 582us/step 445/445 [==============================] - 0s 585us/step Evaluación Entrenamiento MAE: 53.23556900024414 Evaluación Validación MAE: 62.01028060913086 Evaluación Prueba MAE: 70.5667953491211 Entrenamiento RMSE: 57.698856353759766 Validación RMSE: 62.708290100097656 Prueba RMSE: 71.37937927246094
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_83[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_1_CRNN_83 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_83 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_83 = test_eval
train_rmse_measurement_hypo_hype_1_CRNN_83 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_83 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_83 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1778/1778 [==============================] - 3s 2ms/step - loss: 120.6078 - val_loss: 107.7612 Epoch 2/20 1778/1778 [==============================] - 2s 1ms/step - loss: 73.8998 - val_loss: 60.6866 Epoch 3/20 1778/1778 [==============================] - 2s 1ms/step - loss: 48.1719 - val_loss: 37.4952 Epoch 4/20 1778/1778 [==============================] - 2s 1ms/step - loss: 43.6322 - val_loss: 37.3698 Epoch 5/20 1778/1778 [==============================] - 3s 1ms/step - loss: 53.7111 - val_loss: 52.6472 Epoch 6/20 1778/1778 [==============================] - 2s 1ms/step - loss: 51.1199 - val_loss: 50.6407 Epoch 7/20 1778/1778 [==============================] - 2s 1ms/step - loss: 58.1492 - val_loss: 60.7080 3556/3556 [==============================] - 2s 539us/step 445/445 [==============================] - 0s 542us/step 445/445 [==============================] - 0s 527us/step Evaluación Entrenamiento MAE: 52.06108093261719 Evaluación Validación MAE: 60.70766830444336 Evaluación Prueba MAE: 69.26417541503906 Entrenamiento RMSE: 56.53105163574219 Validación RMSE: 61.42062759399414 Prueba RMSE: 70.09201049804688
Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas CRNN utilizadas son:
Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_83 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_CRNN_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_CRNN_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_CRNN_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_83
tabla_1_CRNN_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_83
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 52.061081 |
| Validación MAE | 60.707668 |
| Test MAE | 69.264175 |
| Entrenamiento RMSE | 56.531052 |
| Validación RMSE | 61.420628 |
| Test RMSE | 70.092010 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_CRNN_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_CRNN_83 = pd.concat([tabla_1_CRNN_83, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_83 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_CRNN_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_CRNN_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_CRNN_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_83
tabla_2_CRNN_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_83
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 53.235569 |
| Validación MAE | 62.010281 |
| Test MAE | 70.566795 |
| Entrenamiento RMSE | 57.698856 |
| Validación RMSE | 62.708290 |
| Test RMSE | 71.379379 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_CRNN_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_CRNN_83 = pd.concat([tabla_2_CRNN_83, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_83 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_CRNN_83.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_83.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_83.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_CRNN_83.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_83.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_83.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_CRNN_83.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_83
tabla_3_CRNN_83.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_83
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_83.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 11.620801 |
| Validación MAE | 9.118828 |
| Test MAE | 12.937152 |
| Entrenamiento RMSE | 18.157084 |
| Validación RMSE | 10.958942 |
| Test RMSE | 14.029103 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_83.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_CRNN_83 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_CRNN_83 = pd.concat([tabla_3_CRNN_83, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_83, tabla_2_CRNN_83, tabla_3_CRNN_83], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 52.061081 | Measurement + Hypo_Hyper | Entrenamiento MAE | 53.235569 | Measurement + Hypo_Hyper | Entrenamiento MAE | 11.620801 | Measurement + Hypo_Hyper |
| Validación MAE | 60.707668 | Measurement + Hypo_Hyper | Validación MAE | 62.010281 | Measurement + Hypo_Hyper | Validación MAE | 9.118828 | Measurement + Hypo_Hyper |
| Test MAE | 69.264175 | Measurement + Hypo_Hyper | Test MAE | 70.566795 | Measurement + Hypo_Hyper | Test MAE | 12.937152 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 56.531052 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 57.698856 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 18.157084 | Measurement + Hypo_Hyper |
| Validación RMSE | 61.420628 | Measurement + Hypo_Hyper | Validación RMSE | 62.708290 | Measurement + Hypo_Hyper | Validación RMSE | 10.958942 | Measurement + Hypo_Hyper |
| Test RMSE | 70.092010 | Measurement + Hypo_Hyper | Test RMSE | 71.379379 | Measurement + Hypo_Hyper | Test RMSE | 14.029103 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_CRNN_83.loc[tabla_1_CRNN_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_CRNN_83.loc[tabla_1_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_CRNN_83.loc[tabla_1_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_CRNN_83.loc[tabla_2_CRNN_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_CRNN_83.loc[tabla_2_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_CRNN_83.loc[tabla_2_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_CRNN_83.loc[tabla_3_CRNN_83['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_CRNN_83.loc[tabla_3_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_CRNN_83.loc[tabla_3_CRNN_83['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
CRNN_83 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_83}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 11.620801 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 9.118828 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 12.937152 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 18.157084 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 10.958942 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 14.029103 | Measurement + Hypo_Hyper | 3 |
La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.
Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.
La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.
En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con las características de entrada Measurement + Hypo_Hyper.
from IPython.display import display, HTML
html_tables_83 = f"""
<style>
.table-wrapper {{
display: inline-block;
font-size: 14px;
vertical-align: top;
margin-right: 20px;
}}
.table-wrapper table {{
font-size: 14px;
}}
.table-wrapper th, .table-wrapper td {{
width: 100px;
}}
.table-wrapper h3 {{
text-align: center;
font-size: 18px;
color: blue;
}}
h2 {{
text-align: center;
}}
</style>
<h2>Paciente 83</h2>
<div class="table-wrapper">
<h3>LSTM</h3>
{LSTM_83}
</div>
<div class="table-wrapper">
<h3>CRNN</h3>
{CRNN_83}
</div>
"""
display(HTML(html_tables_83))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 0.862907 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.610178 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.465580 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 3.425832 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 1.738084 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.239297 | Measurement + Hypo_Hyper | 2 |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 11.620801 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 9.118828 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 12.937152 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 18.157084 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 10.958942 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 14.029103 | Measurement + Hypo_Hyper | 3 |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_83': 1, 'tabla_2_CRNN_83': 2, 'tabla_3_CRNN_83': 3}
tables_arch2 = {'tabla_1_LSTM_83': 1, 'tabla_2_LSTM_83': 2, 'tabla_3_LSTM_83': 3}
# Para cada fila
for row in rows:
min_vals = []
# Arquitectura 1
for table_name, arch in tables_arch1.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'CRNN', arch))
# Arquitectura 2
for table_name, arch in tables_arch2.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'LSTM', arch))
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [min_arch],
'Arquitectura (Hiperparámetros)': [min_num]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 83)</span></center>"
display(HTML(html_text))
# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()
# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))
# Nombrando
styled_result4 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result4 = styled_result4.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_83.style.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"
# Muestra el HTML
display(HTML(html))
# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))
# Mostrar la figura
display(fig_hypo_hype_2_LSTM_83)
# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_83.style.set_properties(**{'text-align': 'center'}).hide(axis='index')
# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"
# Mostrar la tabla centrada
display(HTML(table_html))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 0.862907 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.610178 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.465580 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 3.425832 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 1.738084 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.239297 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-03-14 11:00:00 | nan | 166.793915 |
| 2022-03-14 11:15:00 | nan | 165.903671 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 14216 | 100.00% |
| B | 0 | 0.00% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
Los resultados nos aclara que la mejor opción para el paciente 83 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.
Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):
La conclusión final para el paciente 83 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.
La comparativa de soluciones con los modelos RNN y CRNN se va a realizar para el paciente 92 debido a que tras un análisis extenso se ha comprobado que es el 5º con mayor número de mediciones.
Se va a llevar a cabo la comparativa de soluciones con los modelos RNN y CRNN pero con la mejor solución ya obtenida con el paciente 22. De esta manera se va a trabajar con la mejor arquitectura de hiperparámetros y de características de entrada así se va a poder confirmar que el experimento se ha realizado con éxito.
Las 3 arquitecturas de hiperparámetros de LSTM y CRNN son:
Arquitectura de hiperparámetros de LSTM:
Arquitectura de hiperparámetros de CRNN:
La característica de entrada va a ser: 'Measurement + Hypo_Hyper'
# Filtrar los datos para el paciente 92
df_paciente_92 = df_top_5_pacientes[df_top_5_pacientes['Patient_ID'] == 92]
# Imprimir los resultados
df_paciente_92
| Patient_ID | Measurement_date | Measurement_time | Measurement | In_Range | Hypoglycemia | Hyperglycemia | Hour_of_Day | Day_of_Week | |
|---|---|---|---|---|---|---|---|---|---|
| Datetime | |||||||||
| 2018-06-06 11:30:00 | 92 | 2018-06-06 | 1900-01-01 11:38:00 | 372 | False | False | True | 11 | 2 |
| 2018-06-06 11:45:00 | 92 | 2018-06-06 | 1900-01-01 11:53:00 | 365 | False | False | True | 11 | 2 |
| 2018-06-06 12:00:00 | 92 | 2018-06-06 | 1900-01-01 12:08:00 | 345 | False | False | True | 12 | 2 |
| 2018-06-06 12:15:00 | 92 | 2018-06-06 | 1900-01-01 12:23:00 | 327 | False | False | True | 12 | 2 |
| 2018-06-06 12:30:00 | 92 | 2018-06-06 | 1900-01-01 12:38:00 | 305 | False | False | True | 12 | 2 |
| ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
| 2022-01-10 12:00:00 | 92 | 2022-01-10 | 1900-01-01 12:00:00 | 177 | True | False | False | 12 | 0 |
| 2022-01-10 13:15:00 | 92 | 2022-01-10 | 1900-01-01 13:15:00 | 174 | True | False | False | 13 | 0 |
| 2022-01-10 15:00:00 | 92 | 2022-01-10 | 1900-01-01 15:00:00 | 186 | True | False | False | 15 | 0 |
| 2022-01-10 15:45:00 | 92 | 2022-01-10 | 1900-01-01 15:45:00 | 190 | False | False | True | 15 | 0 |
| 2022-01-10 16:00:00 | 92 | 2022-01-10 | 1900-01-01 16:00:00 | 193 | False | False | True | 16 | 0 |
8509 rows × 9 columns
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo LSTM con las variables 'Mesurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo LSTM:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-06-06 11:38:00 | 372 | False | True |
| 2018-06-06 11:53:00 | 365 | False | True |
| 2018-06-06 12:08:00 | 345 | False | True |
| 2018-06-06 12:23:00 | 327 | False | True |
| 2018-06-06 12:38:00 | 305 | False | True |
| ... | ... | ... | ... |
| 2022-01-10 12:00:00 | 177 | False | False |
| 2022-01-10 13:15:00 | 174 | False | False |
| 2022-01-10 15:00:00 | 186 | False | False |
| 2022-01-10 15:45:00 | 190 | False | True |
| 2022-01-10 16:00:00 | 193 | False | True |
8509 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(64, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
Model: "sequential_39"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_48 (LSTM) (None, 64) 17408
dense_39 (Dense) (None, 1) 65
=================================================================
Total params: 17,473
Trainable params: 17,473
Non-trainable params: 0
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1577/1577 [==============================] - 5s 3ms/step - loss: 2.9617 - val_loss: 0.7359 Epoch 2/20 1577/1577 [==============================] - 5s 3ms/step - loss: 2.6971 - val_loss: 0.2157 Epoch 3/20 1577/1577 [==============================] - 5s 3ms/step - loss: 2.3773 - val_loss: 0.7206 Epoch 4/20 1577/1577 [==============================] - 5s 3ms/step - loss: 2.0849 - val_loss: 0.1924 Epoch 5/20 1577/1577 [==============================] - 5s 3ms/step - loss: 1.9256 - val_loss: 0.7419 Epoch 6/20 1577/1577 [==============================] - 5s 3ms/step - loss: 1.9050 - val_loss: 1.4811 Epoch 7/20 1577/1577 [==============================] - 5s 3ms/step - loss: 1.9441 - val_loss: 0.6038
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_2_LSTM_92 = train_eval
val_eval_measurement_hypo_hype_2_LSTM_92 = val_eval
test_eval_measurement_hypo_hype_2_LSTM_92 = test_eval
train_rmse_measurement_hypo_hype_2_LSTM_92 = train_rmse
val_rmse_measurement_hypo_hype_2_LSTM_92 = val_rmse
test_rmse_measurement_hypo_hype_2_LSTM_92 = test_rmse
3154/3154 [==============================] - 3s 862us/step 394/394 [==============================] - 0s 839us/step 394/394 [==============================] - 0s 887us/step Evaluación Entrenamiento MAE: 1.679124116897583 Evaluación Validación MAE: 0.6037915945053101 Evaluación Prueba MAE: 0.6461104154586792 Entrenamiento RMSE: 5.643665313720703 Validación RMSE: 0.7743025422096252 Prueba RMSE: 1.2647500038146973
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
# Almacenar dato
future_predictions_hypo_hype_2_LSTM_92 = future_predictions
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-01-10 16:15:00 NaN 186.222000
2022-01-10 16:30:00 NaN 185.486877
def clarke_error_grid(ref_values, pred_values):
assert (len(ref_values) == len(pred_values)), "Unequal number of values (reference: {}) (prediction: {}).".format(len(ref_values), len(pred_values))
if max(ref_values) > 400 or max(pred_values) > 400:
print("Input Warning: the maximum reference value {} or the maximum prediction value {} exceeds the normal physiological range of glucose (<400 mg/dl).".format(max(ref_values), max(pred_values)))
if min(ref_values) < 0 or min(pred_values) < 0:
print("Input Warning: the minimum reference value {} or the minimum prediction value {} is less than 0 mg/dl.".format(min(ref_values), min(pred_values)))
plt.clf()
plt.scatter(ref_values, pred_values, marker='o', color='black', s=8)
plt.title("Clarke Error Grid")
plt.xlabel("Reference Concentration (mg/dl)")
plt.ylabel("Prediction Concentration (mg/dl)")
plt.xticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.yticks([0, 50, 100, 150, 200, 250, 300, 350, 400])
plt.gca().set_facecolor('white')
plt.gca().set_xlim([0, 400])
plt.gca().set_ylim([0, 400])
plt.gca().set_aspect((400)/(400))
plt.plot([0, 400], [0, 400], ':', c='black')
plt.plot([0, 175/3], [70, 70], '-', c='black')
plt.plot([175/3, 400/1.2], [70, 400], '-', c='black')
plt.plot([70, 70], [84, 400],'-', c='black')
plt.plot([0, 70], [180, 180], '-', c='black')
plt.plot([70, 290], [180, 400],'-', c='black')
plt.plot([70, 70], [0, 56], '-', c='black')
plt.plot([70, 400], [56, 320],'-', c='black')
plt.plot([180, 180], [0, 70], '-', c='black')
plt.plot([180, 400], [70, 70], '-', c='black')
plt.plot([240, 240], [70, 180],'-', c='black')
plt.plot([240, 400], [180, 180], '-', c='black')
plt.plot([130, 180], [0, 70], '-', c='black')
plt.text(30, 15, "A", fontsize=15)
plt.text(370, 260, "B", fontsize=15)
plt.text(280, 370, "B", fontsize=15)
plt.text(160, 370, "C", fontsize=15)
plt.text(160, 15, "C", fontsize=15)
plt.text(30, 140, "D", fontsize=15)
plt.text(370, 120, "D", fontsize=15)
plt.text(30, 370, "E", fontsize=15)
plt.text(370, 15, "E", fontsize=15)
zone = [0] * 5
for i in range(len(ref_values)):
if (ref_values[i] <= 70 and pred_values[i] <= 70) or (pred_values[i] <= 1.2 * ref_values[i] and pred_values[i] >= 0.8 * ref_values[i]):
zone[0] += 1 # Zone A
elif (ref_values[i] >= 180 and pred_values[i] <= 70) or (ref_values[i] <= 70 and pred_values[i] >= 180):
zone[4] += 1 # Zone E
elif ((ref_values[i] >= 70 and ref_values[i] <= 290) and pred_values[i] >= ref_values[i] + 110) or ((ref_values[i] >= 130 and ref_values[i] <= 180) and (pred_values[i] <= (7/5) * ref_values[i] - 182)):
zone[2] += 1 # Zone C
elif (ref_values[i] >= 240 and (pred_values[i] >= 70 and pred_values[i] <= 180)) or (ref_values[i] <= 175/3 and pred_values[i] <= 180 and pred_values[i] >= 70) or ((ref_values[i] >= 175/3 and ref_values[i] <= 70) and pred_values[i] >= (6/5) * ref_values[i]):
zone[3] += 1 # Zone D
else:
zone[1] += 1 # Zone B
return plt, zone
ref_values = y_test
pred_values = y_pred_test
plt, zone = clarke_error_grid(ref_values, pred_values)
# Asignar la figura a una variable antes de llamar a plt.show()
fig_hypo_hype_2_LSTM_92 = plt.gcf()
# Mostrar la figura
plt.show()
# Crear DataFrame para visualizar el conteo de zonas
zone_df = pd.DataFrame({'Zona': ['A', 'B', 'C', 'D', 'E'], 'Conteo': zone})
# Calcular la proporción de cada zona respecto al total
total = sum(zone)
zone_df['Proporción'] = zone_df['Conteo'] / total
# Mostrar la proporción en porcentaje
zone_df['Proporción'] = zone_df['Proporción'].apply(lambda x: '{:.2f}%'.format(x * 100))
# Asignar el DataFrame a una variable
zone_df_hypo_hype_2_LSTM_92 = zone_df
# Ocultar el índice y mostrar el DataFrame
print("Conteo de Zonas:")
display(zone_df_hypo_hype_2_LSTM_92.style.hide(axis="index"))
Conteo de Zonas:
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12607 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_1_LSTM_92 = train_eval
val_eval_measurement_hypo_hype_1_LSTM_92 = val_eval
test_eval_measurement_hypo_hype_1_LSTM_92 = test_eval
train_rmse_measurement_hypo_hype_1_LSTM_92 = train_rmse
val_rmse_measurement_hypo_hype_1_LSTM_92 = val_rmse
test_rmse_measurement_hypo_hype_1_LSTM_92 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_40"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_49 (LSTM) (None, 32) 4608
dense_40 (Dense) (None, 1) 33
=================================================================
Total params: 4,641
Trainable params: 4,641
Non-trainable params: 0
_________________________________________________________________
Epoch 1/20
1577/1577 [==============================] - 4s 2ms/step - loss: 141.0571 - val_loss: 129.8837
Epoch 2/20
1577/1577 [==============================] - 3s 2ms/step - loss: 99.4321 - val_loss: 84.2195
Epoch 3/20
1577/1577 [==============================] - 3s 2ms/step - loss: 61.1622 - val_loss: 46.2528
Epoch 4/20
1577/1577 [==============================] - 3s 2ms/step - loss: 32.1005 - val_loss: 20.4414
Epoch 5/20
1577/1577 [==============================] - 3s 2ms/step - loss: 20.0603 - val_loss: 13.3125
Epoch 6/20
1577/1577 [==============================] - 3s 2ms/step - loss: 13.3559 - val_loss: 10.4139
Epoch 7/20
1577/1577 [==============================] - 3s 2ms/step - loss: 6.8749 - val_loss: 8.3733
Epoch 8/20
1577/1577 [==============================] - 3s 2ms/step - loss: 4.9976 - val_loss: 5.2236
Epoch 9/20
1577/1577 [==============================] - 3s 2ms/step - loss: 3.8893 - val_loss: 3.1057
Epoch 10/20
1577/1577 [==============================] - 3s 2ms/step - loss: 3.0643 - val_loss: 1.0371
Epoch 11/20
1577/1577 [==============================] - 3s 2ms/step - loss: 2.2475 - val_loss: 1.1954
Epoch 12/20
1577/1577 [==============================] - 3s 2ms/step - loss: 2.2237 - val_loss: 1.1394
Epoch 13/20
1577/1577 [==============================] - 3s 2ms/step - loss: 2.1907 - val_loss: 1.5419
3154/3154 [==============================] - 2s 609us/step
394/394 [==============================] - 0s 620us/step
394/394 [==============================] - 0s 637us/step
Evaluación Entrenamiento MAE: 4.471574783325195
Evaluación Validación MAE: 1.5418845415115356
Evaluación Prueba MAE: 2.2097952365875244
Entrenamiento RMSE: 8.106847763061523
Validación RMSE: 1.9643903970718384
Prueba RMSE: 3.0689289569854736
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura LSTM
model = Sequential()
model.add(LSTM(32, return_sequences=True, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(LSTM(64, return_sequences=False, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001)))
model.add(BatchNormalization())
model.add(Dropout(0.5)) # Aumentar la tasa de dropout
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_LSTM_92 = train_eval
val_eval_measurement_hypo_hype_3_LSTM_92 = val_eval
test_eval_measurement_hypo_hype_3_LSTM_92 = test_eval
train_rmse_measurement_hypo_hype_3_LSTM_92 = train_rmse
val_rmse_measurement_hypo_hype_3_LSTM_92 = val_rmse
test_rmse_measurement_hypo_hype_3_LSTM_92 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Model: "sequential_41"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
lstm_50 (LSTM) (None, 8, 32) 4608
batch_normalization_26 (Bat (None, 8, 32) 128
chNormalization)
dropout_26 (Dropout) (None, 8, 32) 0
lstm_51 (LSTM) (None, 64) 24832
batch_normalization_27 (Bat (None, 64) 256
chNormalization)
dropout_27 (Dropout) (None, 64) 0
dense_41 (Dense) (None, 1) 65
=================================================================
Total params: 29,889
Trainable params: 29,697
Non-trainable params: 192
_________________________________________________________________
Epoch 1/20
1577/1577 [==============================] - 7s 4ms/step - loss: 45.2399 - val_loss: 12.0705
Epoch 2/20
1577/1577 [==============================] - 6s 4ms/step - loss: 26.0424 - val_loss: 5.7474
Epoch 3/20
1577/1577 [==============================] - 6s 4ms/step - loss: 25.5011 - val_loss: 5.9949
Epoch 4/20
1577/1577 [==============================] - 6s 4ms/step - loss: 25.3872 - val_loss: 7.8347
Epoch 5/20
1577/1577 [==============================] - 6s 4ms/step - loss: 25.2674 - val_loss: 2.7759
Epoch 6/20
1577/1577 [==============================] - 6s 4ms/step - loss: 24.9938 - val_loss: 14.1727
Epoch 7/20
1577/1577 [==============================] - 7s 4ms/step - loss: 24.9557 - val_loss: 16.8441
Epoch 8/20
1577/1577 [==============================] - 6s 4ms/step - loss: 24.6701 - val_loss: 11.9341
3154/3154 [==============================] - 4s 1ms/step
394/394 [==============================] - 0s 1ms/step
394/394 [==============================] - 0s 1ms/step
Evaluación Entrenamiento MAE: 12.527313232421875
Evaluación Validación MAE: 11.934123039245605
Evaluación Prueba MAE: 14.363471984863281
Entrenamiento RMSE: 15.320870399475098
Validación RMSE: 13.936296463012695
Prueba RMSE: 15.432523727416992
Después de realizar las pruebas se puede determinar que arquitectura del modelo LSTM y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas LSTM utilizadas son:
Podemos ver que según la arquitectura LSTM, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura LSTM y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_LSTM_92 vacía
tabla_1_LSTM_92 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_LSTM_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_LSTM_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_LSTM_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_LSTM_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_LSTM_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_LSTM_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_LSTM_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_LSTM_92
tabla_1_LSTM_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_LSTM_92
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_LSTM_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 4.471575 |
| Validación MAE | 1.541885 |
| Test MAE | 2.209795 |
| Entrenamiento RMSE | 8.106848 |
| Validación RMSE | 1.964390 |
| Test RMSE | 3.068929 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_LSTM_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_LSTM_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_LSTM_92 = pd.concat([tabla_1_LSTM_92, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla vacía
tabla_2_LSTM_92 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_LSTM_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_LSTM_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_LSTM_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_LSTM_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_LSTM_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_LSTM_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_LSTM_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_LSTM_92
tabla_2_LSTM_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_LSTM_92
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 2, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_LSTM_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 1.679124 |
| Validación MAE | 0.603792 |
| Test MAE | 0.646110 |
| Entrenamiento RMSE | 5.643665 |
| Validación RMSE | 0.774303 |
| Test RMSE | 1.264750 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_LSTM_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_LSTM_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_LSTM_92 = pd.concat([tabla_2_LSTM_92, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_LSTM_92 vacía
tabla_3_LSTM_92 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_LSTM_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_LSTM_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_LSTM_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_LSTM_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_LSTM_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_LSTM_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_LSTM_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_LSTM_92
tabla_3_LSTM_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_LSTM_92
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_LSTM_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 12.527313 |
| Validación MAE | 11.934123 |
| Test MAE | 14.363472 |
| Entrenamiento RMSE | 15.320870 |
| Validación RMSE | 13.936296 |
| Test RMSE | 15.432524 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_LSTM_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_LSTM_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_LSTM_92 = pd.concat([tabla_3_LSTM_92, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_LSTM_92, tabla_2_LSTM_92, tabla_3_LSTM_92], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, LSTM</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 4.471575 | Measurement + Hypo_Hyper | Entrenamiento MAE | 1.679124 | Measurement + Hypo_Hyper | Entrenamiento MAE | 12.527313 | Measurement + Hypo_Hyper |
| Validación MAE | 1.541885 | Measurement + Hypo_Hyper | Validación MAE | 0.603792 | Measurement + Hypo_Hyper | Validación MAE | 11.934123 | Measurement + Hypo_Hyper |
| Test MAE | 2.209795 | Measurement + Hypo_Hyper | Test MAE | 0.646110 | Measurement + Hypo_Hyper | Test MAE | 14.363472 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 8.106848 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 5.643665 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 15.320870 | Measurement + Hypo_Hyper |
| Validación RMSE | 1.964390 | Measurement + Hypo_Hyper | Validación RMSE | 0.774303 | Measurement + Hypo_Hyper | Validación RMSE | 13.936296 | Measurement + Hypo_Hyper |
| Test RMSE | 3.068929 | Measurement + Hypo_Hyper | Test RMSE | 1.264750 | Measurement + Hypo_Hyper | Test RMSE | 15.432524 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_LSTM_92.loc[tabla_1_LSTM_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_LSTM_92.loc[tabla_1_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_LSTM_92.loc[tabla_1_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_LSTM_92.loc[tabla_2_LSTM_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_LSTM_92.loc[tabla_2_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_LSTM_92.loc[tabla_2_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_LSTM_92.loc[tabla_3_LSTM_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_LSTM_92.loc[tabla_3_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_LSTM_92.loc[tabla_3_LSTM_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, LSTM</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 2 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_result = result.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_result = styled_result.set_table_styles([
{'selector': 'td:not(:first-child), th:not(:first-child)', 'props': [('text-align', 'center')]}
])
styled_result = styled_result.applymap(bold_rows, subset=['Evaluación+Métrica'])
LSTM_92 = styled_result.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{LSTM_92}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.679124 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.603792 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.646110 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 5.643665 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 0.774303 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.264750 | Measurement + Hypo_Hyper | 2 |
La LSTM (Long Short-Term Memory) es un tipo de red neuronal recurrente diseñada para modelar y aprender patrones en secuencias de datos. Se utiliza mucho para tareas relacionadas con secuencias, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
La LSTM no sufre degradación del gradiente ni dificultades en la captura de dependencias a largo plazo debido a que utilizan una estructura interna de neuronas que les permite tener una memoria de información relevante a larga plazo y olvidar información obsoleta.
La LSTM tiene neuronas y cada una de las neuronas tiene tres puertas principales:
El diseño de las LSTMs permite que las unidades LSTM mantengan una memoria a largo plazo de la información relevante y eviten que la información se diluya a medida que se procesa a través de la secuencia. Esto las hace especialmente efectivas para modelar dependencias a largo plazo y capturar patrones complejos en datos secuenciales.
En resumen, una LSTM es una unidad recurrente que utiliza una estructura de neuronas con puertas de entrada, olvido y salida para modelar y aprender patrones en secuencias de datos. Su diseño les permite recordar información relevante a largo plazo y superar los problemas de degradación del gradiente, permitiendo un procesamiento efectivo de secuencias.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo LSTM es entrenado, se puede determinar que la arquitectura 2 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con LSTM con las características de entrada Measurement + Hypo_Hyper.
A continuación se realiza los pasos correspondientes para su entrenamiento.
En este caso se esta entrenando al algoritmo CRNN con las variables 'Measurement', 'Hypoglycemia' y 'Hyperglycemia'.
Explicación de cada uno de los hiperparámetros utilizados en el modelo CRNN:
import pandas as pd
from sklearn.preprocessing import MinMaxScaler
from keras.models import Sequential
from keras.layers import LSTM, Dense
from keras.callbacks import EarlyStopping
from keras.regularizers import L1L2
from keras.optimizers import SGD
from sklearn.metrics import mean_absolute_error
import matplotlib.pyplot as plt
from tensorflow.keras.layers import Conv1D, MaxPooling1D, Flatten
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
df
| Measurement | Hypoglycemia | Hyperglycemia | |
|---|---|---|---|
| Datetime | |||
| 2018-06-06 11:38:00 | 372 | False | True |
| 2018-06-06 11:53:00 | 365 | False | True |
| 2018-06-06 12:08:00 | 345 | False | True |
| 2018-06-06 12:23:00 | 327 | False | True |
| 2018-06-06 12:38:00 | 305 | False | True |
| ... | ... | ... | ... |
| 2022-01-10 12:00:00 | 177 | False | False |
| 2022-01-10 13:15:00 | 174 | False | False |
| 2022-01-10 15:00:00 | 186 | False | False |
| 2022-01-10 15:45:00 | 190 | False | True |
| 2022-01-10 16:00:00 | 193 | False | True |
8509 rows × 3 columns
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', kernel_regularizer=L1L2(l1=0.001, l2=0.001), input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(BatchNormalization())
model.add(MaxPooling1D(pool_size=2))
model.add(Dropout(0.5))
model.add(LSTM(64))
model.add(BatchNormalization())
model.add(Dropout(0.5))
model.add(Dense(1)) # 1 nodo de salida para Medición
model.summary()
# 17. Compilar el modelo
opt = SGD(learning_rate=0.001, momentum=0.9)
model.compile(optimizer=opt, loss='mean_absolute_error')
Model: "sequential_42"
_________________________________________________________________
Layer (type) Output Shape Param #
=================================================================
conv1d_12 (Conv1D) (None, 6, 32) 320
batch_normalization_28 (Bat (None, 6, 32) 128
chNormalization)
max_pooling1d_12 (MaxPoolin (None, 3, 32) 0
g1D)
dropout_28 (Dropout) (None, 3, 32) 0
lstm_52 (LSTM) (None, 64) 24832
batch_normalization_29 (Bat (None, 64) 256
chNormalization)
dropout_29 (Dropout) (None, 64) 0
dense_42 (Dense) (None, 1) 65
=================================================================
Total params: 25,601
Trainable params: 25,409
Non-trainable params: 192
_________________________________________________________________
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
Epoch 1/20 1577/1577 [==============================] - 5s 2ms/step - loss: 44.1603 - val_loss: 19.9735 Epoch 2/20 1577/1577 [==============================] - 3s 2ms/step - loss: 25.6931 - val_loss: 18.6398 Epoch 3/20 1577/1577 [==============================] - 3s 2ms/step - loss: 25.3511 - val_loss: 11.5467 Epoch 4/20 1577/1577 [==============================] - 3s 2ms/step - loss: 24.9358 - val_loss: 21.7919 Epoch 5/20 1577/1577 [==============================] - 3s 2ms/step - loss: 24.9161 - val_loss: 21.1320 Epoch 6/20 1577/1577 [==============================] - 3s 2ms/step - loss: 24.8354 - val_loss: 12.7428
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
# Almacenar datos
# Se almacenan los resultados de evaluación y la raíz del error cuadrático medio (RMSE) del modelo para su posterior uso en una tabla.
train_eval_measurement_hypo_hype_3_CRNN_92 = train_eval
val_eval_measurement_hypo_hype_3_CRNN_92 = val_eval
test_eval_measurement_hypo_hype_3_CRNN_92 = test_eval
train_rmse_measurement_hypo_hype_3_CRNN_92 = train_rmse
val_rmse_measurement_hypo_hype_3_CRNN_92 = val_rmse
test_rmse_measurement_hypo_hype_3_CRNN_92 = test_rmse
3154/3154 [==============================] - 2s 648us/step 394/394 [==============================] - 0s 657us/step 394/394 [==============================] - 0s 716us/step Evaluación Entrenamiento MAE: 23.519376754760742 Evaluación Validación MAE: 12.742785453796387 Evaluación Prueba MAE: 10.551319122314453 Entrenamiento RMSE: 26.978843688964844 Validación RMSE: 15.696503639221191 Prueba RMSE: 14.015483856201172
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
# 23. Crear una lista para almacenar fechas y datos reales y predichos
train_dates = train.index[seq_length+1:]
val_dates = val.index[seq_length+1:]
test_dates = test.index[seq_length+1:]
real_data = np.concatenate([y_train.ravel(), y_val.ravel(), y_test.ravel()])
predicted_data = np.concatenate([y_pred_train.ravel(), y_pred_val.ravel(), y_pred_test.ravel()])
dates = np.concatenate([train_dates,val_dates,test_dates])
# 24. Obtener los últimos datos conocidos para la predicción inicial (El for para hacer múltiples predicciones a intervalos de 15 minutos)
# Realizar predicciones para los próximos 15 minutos
predictions_15min = []
last_sequence = df[['Measurement', 'Hypoglycemia', 'Hyperglycemia']].values[-seq_length:].astype('float32')
for i in range(2): ## Se predice los próximos 15 minutos y los siguientes 15 minutos. Mostrando predicción futura de los 15 y 30 minutos
next_prediction = model.predict(last_sequence.reshape(1, seq_length, 3))[0][0]
predictions_15min.append(next_prediction)
last_sequence = np.append(last_sequence[1:], [[next_prediction, last_sequence[-1][1], last_sequence[-1][2]]], axis=0) # Aquí también se añade la última característica
# Crear fechas para los próximos 15 minutos y el número de iteraciones "2" más una fila adicional para los datos conocidos
last_date = df.index[-1]
future_dates = pd.date_range(start=last_date, periods=3, freq='15min')[1:]
# Agregar las predicciones futuras al DataFrame de resultados
future_predictions = pd.DataFrame({'date': future_dates, 'Datos reales': np.nan, 'Datos predichos': predictions_15min})
future_predictions.set_index('date', inplace=True)
results = pd.DataFrame({'date': dates, 'Datos reales': real_data, 'Datos predichos': predicted_data})
results.set_index('date', inplace=True)
results = pd.concat([results, future_predictions])
# Graficar los resultados con las predicciones futuras
ax = results.plot(figsize=(14,7))
results.iloc[-2:]['Datos predichos'].plot(ax=ax, style='r', label='Predicciones futuras')
plt.ylabel('Medición')
plt.xlabel('Fecha')
plt.legend()
print(f'\n{future_predictions}')
plt.show()
1/1 [==============================] - 0s 10ms/step
1/1 [==============================] - 0s 10ms/step
Datos reales Datos predichos
date
2022-01-10 16:15:00 NaN 169.396500
2022-01-10 16:30:00 NaN 169.985672
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(64, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
model.compile(optimizer='adam', loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_2_CRNN_92 = train_eval
val_eval_measurement_hypo_hype_2_CRNN_92 = val_eval
test_eval_measurement_hypo_hype_2_CRNN_92 = test_eval
train_rmse_measurement_hypo_hype_2_CRNN_92 = train_rmse
val_rmse_measurement_hypo_hype_2_CRNN_92 = val_rmse
test_rmse_measurement_hypo_hype_2_CRNN_92 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1577/1577 [==============================] - 3s 2ms/step - loss: 139.1703 - val_loss: 126.4468 Epoch 2/20 1577/1577 [==============================] - 2s 1ms/step - loss: 102.6864 - val_loss: 90.3819 Epoch 3/20 1577/1577 [==============================] - 2s 1ms/step - loss: 93.2370 - val_loss: 84.4903 Epoch 4/20 1577/1577 [==============================] - 2s 1ms/step - loss: 81.4039 - val_loss: 74.3690 Epoch 5/20 1577/1577 [==============================] - 2s 1ms/step - loss: 73.7009 - val_loss: 72.1209 Epoch 6/20 1577/1577 [==============================] - 2s 1ms/step - loss: 70.5329 - val_loss: 72.3251 Epoch 7/20 1577/1577 [==============================] - 2s 1ms/step - loss: 78.3239 - val_loss: 72.9205 Epoch 8/20 1577/1577 [==============================] - 2s 1ms/step - loss: 79.2571 - val_loss: 74.0319 3154/3154 [==============================] - 2s 552us/step 394/394 [==============================] - 0s 564us/step 394/394 [==============================] - 0s 582us/step Evaluación Entrenamiento MAE: 69.35159301757812 Evaluación Validación MAE: 74.0319595336914 Evaluación Prueba MAE: 79.8343276977539 Entrenamiento RMSE: 76.4446029663086 Validación RMSE: 75.49947357177734 Prueba RMSE: 81.06375885009766
# 1. Seleccionar columnas relevantes para la predicción
df = df_paciente_92[['Measurement_date', 'Measurement_time', 'Measurement', 'Hypoglycemia', 'Hyperglycemia']].copy()
# 2. Crear una columna de fecha y hora combinadas
df['Datetime'] = pd.to_datetime(df['Measurement_date'].dt.date.astype(str) + ' ' + df['Measurement_time'].dt.time.astype(str))
# 3. Establecer 'Datetime' como el índice
df.set_index('Datetime', inplace=True)
# 4. Descartar las columnas 'Measurement_date' y 'Measurement_time'
df = df.drop(columns=['Measurement_date', 'Measurement_time'])
# 5. Resamplear los datos en intervalos de 15 minutos
df_resampled = df.resample('15T').mean()
# 6. Ordenar el DataFrame por fecha antes de realizar el resampleo
df = df.sort_index()
# 7. Rellenar los valores nan con el último valor válido
def fill_nan(df):
df_resampled = df.fillna(method='ffill')
return df_resampled
# 8. Rellenar los valores faltantes
df_filled = fill_nan(df_resampled)
# 9. Verificar si hay algún valor NaN restante
assert not df_filled.isna().any().any(), "There are still NaN values in df_filled"
# 10. Convertir a float32
df_filled = df_filled.astype('float32')
# 11. Dividir el conjunto de datos en entrenamiento (80%), validación (10%) y prueba (10%) (shuffle=False)
train_size = int(len(df_filled) * 0.8)
val_size = int(len(df_filled) * 0.1)
test_size = len(df_filled) - train_size - val_size
train, val, test = df_filled.iloc[0:train_size], df_filled.iloc[train_size:train_size+val_size], df_filled.iloc[train_size+val_size:len(df_filled)]
# 12. Función create_sequences toma un DataFrame 'data' y una longitud de secuencia 'seq_length' como parámetros.
def create_sequences(data, seq_length):
# Inicializamos dos listas vacías para almacenar las secuencias de entrada (xs) y las etiquetas de salida (ys).
xs = []
ys = []
# Iteramos a través de los índices del DataFrame 'data' hasta el índice que sea 'len(data) - seq_length - 1'.
for i in range(len(data)-seq_length-1):
x = data.iloc[i:(i+seq_length)]
y = data.iloc[i+seq_length]['Measurement']
xs.append(x)
ys.append(y)
return np.array(xs), np.array(ys)
# Definir la longitud de la secuencia
seq_length = 8 # Son 2h -> 15 minutos 8 veces
# Crear secuencias para el entrenamiento, validación y prueba
X_train, y_train = create_sequences(train[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_val, y_val = create_sequences(val[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
X_test, y_test = create_sequences(test[['Measurement', 'Hypoglycemia', 'Hyperglycemia']], seq_length)
# 13. Crear un callback de EarlyStopping para detener el entrenamiento cuando la pérdida de validación deja de mejorar
early_stop = EarlyStopping(monitor='val_loss', patience=3)
# 14. Reshape inputs for LSTM [samples, timesteps, features]
X_train = X_train.reshape((X_train.shape[0], X_train.shape[1], 3))
X_val = X_val.reshape((X_val.shape[0], X_val.shape[1], 3))
X_test = X_test.reshape((X_test.shape[0], X_test.shape[1], 3))
# 15. Tamaño del batch
batch_size = 64
# 16. Definir la arquitectura CRNN
model = Sequential()
model.add(Conv1D(32, kernel_size=3, activation='relu', input_shape=(X_train.shape[1], X_train.shape[2])))
model.add(MaxPooling1D(pool_size=2))
model.add(LSTM(32))
model.add(Flatten())
model.add(Dense(1))
# 17. Compilar el modelo
opt = Adam(learning_rate=0.001)
model.compile(optimizer=opt, loss='mean_absolute_error')
# 18. Entrenar el modelo
history = model.fit(
X_train, y_train,
batch_size=batch_size,
epochs=20,
validation_data=(X_val, y_val),
callbacks=[early_stop],
shuffle=False
)
# 19. Realizar predicciones
y_pred_train = model.predict(X_train)
y_pred_val = model.predict(X_val)
y_pred_test = model.predict(X_test)
# 20. Evaluar los conjuntos de entrenamiento, validación y prueba
train_eval = model.evaluate(X_train, y_train, verbose=0)
val_eval = model.evaluate(X_val, y_val, verbose=0)
test_eval = model.evaluate(X_test, y_test, verbose=0)
print(f'Evaluación Entrenamiento MAE: {train_eval}')
print(f'Evaluación Validación MAE: {val_eval}')
print(f'Evaluación Prueba MAE: {test_eval}')
# 21. Calcular la raíz del error cuadrático medio (RMSE) como métrica de evaluación
train_rmse = np.sqrt(mean_squared_error(y_train, y_pred_train))
val_rmse = np.sqrt(mean_squared_error(y_val, y_pred_val))
test_rmse = np.sqrt(mean_squared_error(y_test, y_pred_test))
print(f'Entrenamiento RMSE: {train_rmse}')
print(f'Validación RMSE: {val_rmse}')
print(f'Prueba RMSE: {test_rmse}')
train_eval_measurement_hypo_hype_1_CRNN_92 = train_eval
val_eval_measurement_hypo_hype_1_CRNN_92 = val_eval
test_eval_measurement_hypo_hype_1_CRNN_92 = test_eval
train_rmse_measurement_hypo_hype_1_CRNN_92 = train_rmse
val_rmse_measurement_hypo_hype_1_CRNN_92 = val_rmse
test_rmse_measurement_hypo_hype_1_CRNN_92 = test_rmse
# 22. Graficar los resultados obtenidos en el entrenamiento y validación
plt.plot(history.history['loss'], label='Pérdida de entrenamiento')
plt.plot(history.history['val_loss'], label='Pérdida de validación')
plt.legend()
plt.show()
Epoch 1/20 1577/1577 [==============================] - 3s 2ms/step - loss: 138.9516 - val_loss: 124.8335 Epoch 2/20 1577/1577 [==============================] - 2s 1ms/step - loss: 100.2275 - val_loss: 87.0507 Epoch 3/20 1577/1577 [==============================] - 2s 1ms/step - loss: 77.1367 - val_loss: 70.0232 Epoch 4/20 1577/1577 [==============================] - 2s 1ms/step - loss: 67.3278 - val_loss: 59.3543 Epoch 5/20 1577/1577 [==============================] - 2s 1ms/step - loss: 70.1445 - val_loss: 66.2480 Epoch 6/20 1577/1577 [==============================] - 2s 1ms/step - loss: 96.7198 - val_loss: 90.8945 Epoch 7/20 1577/1577 [==============================] - 2s 1ms/step - loss: 74.3784 - val_loss: 67.8310 3154/3154 [==============================] - 2s 530us/step 394/394 [==============================] - 0s 542us/step 394/394 [==============================] - 0s 577us/step Evaluación Entrenamiento MAE: 63.52792739868164 Evaluación Validación MAE: 67.8309555053711 Evaluación Prueba MAE: 73.63329315185547 Entrenamiento RMSE: 70.96502685546875 Validación RMSE: 69.43001556396484 Prueba RMSE: 74.96488189697266
Después de realizar 3 pruebas se puede determinar que arquitectura del modelo CRNN y con que características de entrada ofrece mejores resultados.
Las características de entrada utilizadas son:
El hecho de hacer la prueba únicamente con Measurement + Hypoglycemia + Hyperglycemia se debe a que en general todas tienen resultados similares, pero esta en concreto en general ofrece mejores resultados.
Las arquitecturas CRNN utilizadas son:
Podemos ver que según la arquitectura CRNN, cada una de las arquitecturas tiene distintos hiperparámetros, ofrecen un rendimiento variado. Además, el resultado obtenido depende de las características de entrada utilizadas, siendo en este caso la Measurement + Hypoglycemia + Hyperglycemia.
Para simplificar la comparación y determinar fácilmente cuál arquitectura brinda el mejor rendimiento, se reune todos los resultados en una tabla. De esta manera, es posible identificar qué configuración de arquitectura CRNN y características de entrada logran un mejor desempeño en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos.
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_1_CRNN vacía
tabla_1_CRNN_92 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_1_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_1_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_1_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_1_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_1_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_1_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_1_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_1_CRNN_92
tabla_1_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_1_CRNN_92
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(2) {
text-align: center;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 1, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_1_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 4.471575 | Measurement + Hypo_Hyper | Entrenamiento MAE | 1.679124 | Measurement + Hypo_Hyper | Entrenamiento MAE | 12.527313 | Measurement + Hypo_Hyper |
| Validación MAE | 1.541885 | Measurement + Hypo_Hyper | Validación MAE | 0.603792 | Measurement + Hypo_Hyper | Validación MAE | 11.934123 | Measurement + Hypo_Hyper |
| Test MAE | 2.209795 | Measurement + Hypo_Hyper | Test MAE | 0.646110 | Measurement + Hypo_Hyper | Test MAE | 14.363472 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 8.106848 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 5.643665 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 15.320870 | Measurement + Hypo_Hyper |
| Validación RMSE | 1.964390 | Measurement + Hypo_Hyper | Validación RMSE | 0.774303 | Measurement + Hypo_Hyper | Validación RMSE | 13.936296 | Measurement + Hypo_Hyper |
| Test RMSE | 3.068929 | Measurement + Hypo_Hyper | Test RMSE | 1.264750 | Measurement + Hypo_Hyper | Test RMSE | 15.432524 | Measurement + Hypo_Hyper |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_1_CRNN_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_1_CRNN_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_1_CRNN_92 = pd.concat([tabla_1_CRNN_92, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_2_CRNN vacía
tabla_2_CRNN_92 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_2_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_2_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_2_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_2_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_2_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_2_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_2_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_2_CRNN_92
tabla_2_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_2_CRNN_92
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_2_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 69.351593 |
| Validación MAE | 74.031960 |
| Test MAE | 79.834328 |
| Entrenamiento RMSE | 76.444603 |
| Validación RMSE | 75.499474 |
| Test RMSE | 81.063759 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_2_CRNN_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_2_CRNN_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_2_CRNN_92 = pd.concat([tabla_2_CRNN_92, pd.DataFrame([row])])
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_92 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_92
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 23.519377 |
| Validación MAE | 12.742785 |
| Test MAE | 10.551319 |
| Entrenamiento RMSE | 26.978844 |
| Validación RMSE | 15.696504 |
| Test RMSE | 14.015484 |
import pandas as pd
from IPython.display import display, HTML
# Crear la tabla_3_CRNN vacía
tabla_3_CRNN_92 = pd.DataFrame(columns=[
'Evaluación+Métrica',
'Measurement + Hypo_Hyper',
])
# Definir los valores en cada posición
tabla_3_CRNN_92.loc[1, 'Evaluación+Métrica'] = '<b>Entrenamiento MAE</b>'
tabla_3_CRNN_92.loc[2, 'Evaluación+Métrica'] = '<b>Validación MAE</b>'
tabla_3_CRNN_92.loc[3, 'Evaluación+Métrica'] = '<b>Test MAE</b>'
tabla_3_CRNN_92.loc[4, 'Evaluación+Métrica'] = '<b>Entrenamiento RMSE</b>'
tabla_3_CRNN_92.loc[5, 'Evaluación+Métrica'] = '<b>Validación RMSE</b>'
tabla_3_CRNN_92.loc[6, 'Evaluación+Métrica'] = '<b>Test RMSE</b>'
tabla_3_CRNN_92.loc[1, 'Measurement + Hypo_Hyper'] = train_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[2, 'Measurement + Hypo_Hyper'] = val_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[3, 'Measurement + Hypo_Hyper'] = test_eval_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[4, 'Measurement + Hypo_Hyper'] = train_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[5, 'Measurement + Hypo_Hyper'] = val_rmse_measurement_hypo_hype_3_CRNN_92
tabla_3_CRNN_92.loc[6, 'Measurement + Hypo_Hyper'] = test_rmse_measurement_hypo_hype_3_CRNN_92
# Usa CSS para definir el ancho de la columna Evaluación+Métrica
css = """
<style>
.dataframe td:nth-child(1) {
width: 165px;
}
</style>
"""
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>TABLA ARQUITECTURA 3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = tabla_3_CRNN_92.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Measurement + Hypo_Hyper |
|---|---|
| Entrenamiento MAE | 23.519377 |
| Validación MAE | 12.742785 |
| Test MAE | 10.551319 |
| Entrenamiento RMSE | 26.978844 |
| Validación RMSE | 15.696504 |
| Test RMSE | 14.015484 |
# Crear una copia de la tabla original y convertir las columnas a tipo numérico
tabla_copia = tabla_3_CRNN_92.copy()
cols_to_convert = ['Measurement + Hypo_Hyper']
for col in cols_to_convert:
tabla_copia[col] = pd.to_numeric(tabla_copia[col], errors='coerce')
# Crear una nueva tabla vacía
tabla_3_CRNN_92 = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente'])
# Obtener los valores mínimos y la columna correspondiente de cada fila en la copia
for i in range(1, 7):
min_val = tabla_copia.loc[i, 'Measurement':].min()
min_col = tabla_copia.loc[i, 'Measurement':].astype(float).idxmin()
row = {
'Evaluación+Métrica': tabla_copia.loc[i, 'Evaluación+Métrica'],
'Valor mínimo': min_val,
'Columna correspondiente': min_col
}
tabla_3_CRNN_92 = pd.concat([tabla_3_CRNN_92, pd.DataFrame([row])])
# Combina las tablas horizontalmente
combined_table = pd.concat([tabla_1_CRNN_92, tabla_2_CRNN_92, tabla_3_CRNN_92], axis=1)
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>COMPARATIVA DE ARQUITECTURA 1,2,3, CRNN</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = combined_table.style.set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:not(:first-child):not(:nth-child(4)):not(:nth-child(7))', 'props': [('text-align', 'center')]},
{'selector': 'th', 'props': [('text-align', 'center')]}
])
table_html = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{table_html}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente | Evaluación+Métrica | Valor mínimo | Columna correspondiente |
|---|---|---|---|---|---|---|---|---|
| Entrenamiento MAE | 63.527927 | Measurement + Hypo_Hyper | Entrenamiento MAE | 69.351593 | Measurement + Hypo_Hyper | Entrenamiento MAE | 23.519377 | Measurement + Hypo_Hyper |
| Validación MAE | 67.830956 | Measurement + Hypo_Hyper | Validación MAE | 74.031960 | Measurement + Hypo_Hyper | Validación MAE | 12.742785 | Measurement + Hypo_Hyper |
| Test MAE | 73.633293 | Measurement + Hypo_Hyper | Test MAE | 79.834328 | Measurement + Hypo_Hyper | Test MAE | 10.551319 | Measurement + Hypo_Hyper |
| Entrenamiento RMSE | 70.965027 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 76.444603 | Measurement + Hypo_Hyper | Entrenamiento RMSE | 26.978844 | Measurement + Hypo_Hyper |
| Validación RMSE | 69.430016 | Measurement + Hypo_Hyper | Validación RMSE | 75.499474 | Measurement + Hypo_Hyper | Validación RMSE | 15.696504 | Measurement + Hypo_Hyper |
| Test RMSE | 74.964882 | Measurement + Hypo_Hyper | Test RMSE | 81.063759 | Measurement + Hypo_Hyper | Test RMSE | 14.015484 | Measurement + Hypo_Hyper |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Para cada fila
for row in rows:
# Encontrar el valor mínimo y la columna correspondiente para cada arquitectura
min_val_1 = tabla_1_CRNN_92.loc[tabla_1_CRNN_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_1_CRNN_92.loc[tabla_1_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_1 = tabla_1_CRNN_92.loc[tabla_1_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_1 = None
min_val_2 = tabla_2_CRNN_92.loc[tabla_2_CRNN_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_2_CRNN_92.loc[tabla_2_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_2 = tabla_2_CRNN_92.loc[tabla_2_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_2 = None
min_val_3 = tabla_3_CRNN_92.loc[tabla_3_CRNN_92['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not tabla_3_CRNN_92.loc[tabla_3_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col_3 = tabla_3_CRNN_92.loc[tabla_3_CRNN_92['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col_3 = None
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val = min(min_val_1, min_val_2, min_val_3)
if min_val == min_val_1:
min_col = min_col_1
architecture = 1
elif min_val == min_val_2:
min_col = min_col_2
architecture = 2
else:
min_col = min_col_3
architecture = 3
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [architecture]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>RESULTADO A MEJOR ARQUITECTURA, CRNN</span></center>"
display(HTML(html_text))
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px;'>ARQUITECTURA 3 <br>(Measurement + Hypo_Hyper)</span></center>"
display(HTML(html_text))
# Aplicar el estilo personalizado al DataFrame
styled_table = result.style.applymap(bold_rows, subset=['Evaluación+Métrica']).set_table_attributes("style='margin-left: auto; margin-right: auto;'")
styled_table = styled_table.set_table_styles([
{'selector': 'td:nth-child(4)', 'props': [('text-align', 'center')]},
{'selector': 'td:nth-child(2)', 'props': [('text-align', 'center')]}
])
# Mostrar el DataFrame resultante con estilo y sin índices
CRNN_92 = styled_table.hide(axis='index').to_html()
# Mostrar la tabla centrada
display(HTML(f'<div style="display: flex; justify-content: center;">{CRNN_92}</div>'))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 23.519377 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 12.742785 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 10.551319 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 26.978844 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 15.696504 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 14.015484 | Measurement + Hypo_Hyper | 3 |
La Red Neuronal Recurrente Convolucional (CRNN) es una arquitectura de red neuronal que combina características de las redes neuronales convolucionales (CNN) y las redes neuronales recurrentes (RNN). Esta diseñada para modelar y aprender patrones en datos secuenciales, al tiempo que tiene en cuenta la estructura espacial de los datos, siendo en este caso una buena elección dada la serie temporal que se utiliza para la predicción de los niveles de glucosa.
Al igual que las CNN, las CRNN utilizan capas convolucionales para extraer características locales de los datos de entrada. Estas capas convolucionales aplican filtros a ventanas de tamaño fijo, que se deslizan a lo largo de la secuencia para detectar patrones locales. Esto permite a la CRNN capturar características relevantes en diferentes partes de la secuencia de datos.
Sin embargo, a diferencia de las CNN tradicionales, las CRNN también incorporan la capacidad de modelar dependencias a largo plazo utilizando unidades recurrentes, como las LSTM. Las LSTM en la CRNN actúan como una capa de procesamiento secuencial adicional que se aplica después de las capas convolucionales. Estas unidades recurrentes permiten que la red mantenga una memoria de largo plazo y capture dependencias a largo plazo en la secuencia.
La combinación de capas convolucionales y unidades LSTM en una CRNN aprovecha tanto la capacidad de las CNN para extraer características locales como la capacidad de las RNN para modelar dependencias a largo plazo. Las capas convolucionales ayudan a capturar patrones locales en la secuencia, mientras que las unidades LSTM permiten que la red aprenda y recuerde dependencias a largo plazo entre los elementos de la secuencia.
En resumen, una CRNN es una arquitectura de red neuronal que combina capas convolucionales y unidades LSTM para modelar y aprender patrones en datos secuenciales. Las capas convolucionales extraen características locales de la secuencia, mientras que las unidades LSTM capturan dependencias a largo plazo. Esto permite a la CRNN capturar patrones complejos y modelar tanto la estructura espacial como las dependencias temporales en los datos secuenciales.
Las fases de evaluación MAE y métrica RMSE utilizadas son:
Análisis de los resultados:
En el análisis de los resultados hay que tener en cuenta que al ser un algoritmo de aprendizaje automático se va a obtener distintos resultados en cada entrenamiento.
Las razones de los resultados distintos son varias y son las siguientes:
En resumen, hay múltiples factores que contribuyen a que cada ejecución de entrenamiento produzca resultados diferentes. La aleatoriedad en la inicialización, el muestreo de datos, las técnicas de regularización, los hiperparámetros y la sensibilidad a las condiciones iniciales y al ruido son algunos de los factores más comunes que generan variabilidad en los resultados de loss.
En el análisis de los resultados se puede observar que los resultados obtenidos son bastante similares, con valores bajos, tanto para la Evaluación MAE como para la métrica RMSE.
A pesar de que hay pequeñas variaciones en los resultados cada vez que el modelo CRNN es entrenado, se puede determinar que la arquitectura 3 obtiene mejores resultados con las características de entrada Measurement + Hypo_Hyper.
En un análisis global arquitectura:
En un análisis global características de entrada:
La conclusión a la que se ha llegado tras las distintas evaluaciones y comparativas se ha determinado que la mejor arquitectura es la 2 con LSTM con las características de entrada Measurement + Hypo_Hyper.
from IPython.display import display, HTML
html_tables_92 = f"""
<style>
.table-wrapper {{
display: inline-block;
font-size: 14px;
vertical-align: top;
margin-right: 20px;
}}
.table-wrapper table {{
font-size: 14px;
}}
.table-wrapper th, .table-wrapper td {{
width: 100px;
}}
.table-wrapper h3 {{
text-align: center;
font-size: 18px;
color: blue;
}}
h2 {{
text-align: center;
}}
</style>
<h2>Paciente 92</h2>
<div class="table-wrapper">
<h3>LSTM</h3>
{LSTM_92}
</div>
<div class="table-wrapper">
<h3>CRNN</h3>
{CRNN_92}
</div>
"""
display(HTML(html_tables_92))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 1.679124 | Measurement + Hypo_Hyper | 2 |
| Validación MAE | 0.603792 | Measurement + Hypo_Hyper | 2 |
| Test MAE | 0.646110 | Measurement + Hypo_Hyper | 2 |
| Entrenamiento RMSE | 5.643665 | Measurement + Hypo_Hyper | 2 |
| Validación RMSE | 0.774303 | Measurement + Hypo_Hyper | 2 |
| Test RMSE | 1.264750 | Measurement + Hypo_Hyper | 2 |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura |
|---|---|---|---|
| Entrenamiento MAE | 23.519377 | Measurement + Hypo_Hyper | 3 |
| Validación MAE | 12.742785 | Measurement + Hypo_Hyper | 3 |
| Test MAE | 10.551319 | Measurement + Hypo_Hyper | 3 |
| Entrenamiento RMSE | 26.978844 | Measurement + Hypo_Hyper | 3 |
| Validación RMSE | 15.696504 | Measurement + Hypo_Hyper | 3 |
| Test RMSE | 14.015484 | Measurement + Hypo_Hyper | 3 |
# Crear un nuevo DataFrame para almacenar los resultados
result = pd.DataFrame(columns=['Evaluación+Métrica', 'Valor mínimo', 'Columna correspondiente', 'Arquitectura', 'Arquitectura (Hiperparámetros)'])
# Definir las filas para las que quieres encontrar el valor mínimo global
rows = ['<b>Entrenamiento MAE</b>', '<b>Validación MAE</b>', '<b>Test MAE</b>', '<b>Entrenamiento RMSE</b>', '<b>Validación RMSE</b>', '<b>Test RMSE</b>']
# Diccionarios para almacenar los nombres de las tablas
tables_arch1 = {'tabla_1_CRNN_92': 1, 'tabla_2_CRNN_92': 2, 'tabla_3_CRNN_92': 3}
tables_arch2 = {'tabla_1_LSTM_92': 1, 'tabla_2_LSTM_92': 2, 'tabla_3_LSTM_92': 3}
# Para cada fila
for row in rows:
min_vals = []
# Arquitectura 1
for table_name, arch in tables_arch1.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'CRNN', arch))
# Arquitectura 2
for table_name, arch in tables_arch2.items():
min_val = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Valor mínimo'].min()
if not globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].empty:
min_col = globals()[table_name].loc[globals()[table_name]['Evaluación+Métrica'] == row, 'Columna correspondiente'].iloc[0]
else:
min_col = None
min_vals.append((min_val, min_col, 'LSTM', arch))
# Encontrar el valor mínimo global y la columna y arquitectura correspondientes
min_val, min_col, min_arch, min_num = min(min_vals, key=lambda x: x[0])
# Quitar las etiquetas <b> y </b> de la variable row
row_without_tags = row.replace('<b>', '').replace('</b>', '')
# Agregar una fila al DataFrame result usando pandas.concat
result = pd.concat([result, pd.DataFrame({
'Evaluación+Métrica': [row_without_tags],
'Valor mínimo': [min_val],
'Columna correspondiente': [min_col],
'Arquitectura': [min_arch],
'Arquitectura (Hiperparámetros)': [min_num]
})], ignore_index=True)
# Define una función para aplicar el estilo negrita a las filas especificadas
def bold_rows(x):
if x in ['Entrenamiento MAE', 'Validación MAE', 'Test MAE', 'Entrenamiento RMSE', 'Validación RMSE', 'Test RMSE']:
return 'font-weight: bold'
return ''
# Aplica el CSS y muestra el DataFrame
html_text = "<br><center><span style='font-size:26px; font-weight:bold;'>MEJOR ARQUITECTURA (Paciente 92)</span></center>"
display(HTML(html_text))
# Aplica el estilo personalizado al DataFrame resultante
styled_result = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_result = styled_result.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Mostrar el DataFrame resultante con estilo y sin índices
final_output = styled_result.hide(axis='index').to_html()
# Utilizando la función `display(HTML())` para mostrar el contenido HTML en un div centrado en la página.
display(HTML("<div style='margin: 0 auto; width:70%'>" + final_output + "</div>"))
# Nombrando
styled_result5 = result.style.applymap(bold_rows, subset=['Evaluación+Métrica'])
styled_result5 = styled_result5.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
# Muestra la predicción del nivel de glucosa en los próximos 15 y 30 minutos
# Crea un HTML con el título y los datos
# Aplica el estilo personalizado para centrar los valores de las columnas
styled_table = future_predictions_hypo_hype_2_LSTM_92.style.set_table_styles([{
'selector': 'th, td',
'props': [('text-align', 'center')]
}])
html = """
<div style='text-align: center;'>
<h2>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h2>
<div style='margin: 20px auto 0 auto; width:40%;'>
""" + styled_table.to_html() + "</div></div>"
# Muestra el HTML
display(HTML(html))
# Centrar la figura y la tabla utilizando CSS y HTML
display(HTML("""
<style>
.output_png {
display: table-cell;
text-align: center;
vertical-align: middle;
}
</style>
<h2>Precisión del modelo con Clarke Error Grid</h2>
"""))
# Mostrar la figura
display(fig_hypo_hype_2_LSTM_92)
# Centrar el contenido de la tabla utilizando CSS y ocultar los índices
styled_table = zone_df_hypo_hype_2_LSTM_92.style.set_properties(**{'text-align': 'center'}).hide(axis='index')
# Convertir la tabla en un objeto HTML
table_html = f"<center>{styled_table.to_html()}</center>"
# Mostrar la tabla centrada
display(HTML(table_html))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.679124 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.603792 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.646110 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 5.643665 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 0.774303 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.264750 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-01-10 16:15:00 | nan | 186.222000 |
| 2022-01-10 16:30:00 | nan | 185.486877 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12607 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
Los resultados nos aclara que la mejor opción para el paciente 92 es utilizar una arquitectura LSTM con unos hiperparámetros llamados Arquitectura 2.
Arquitectura LSTM con la arquitectura 2 (Mejor hiperparámetros en opción global):
La conclusión final para el paciente 92 es utilizar la arquitectura LSTM con la arquitectura 2 y las características de entrada Measurement + Hypo_Hyper para la predicción de sus niveles de glucosa para los próximos 15 y 30 minutos ya que nos arrojan unos resultados muy buenos.
# Definir los títulos
titles = ["MEJOR ARQUITECTURA (Paciente 22)", "MEJOR ARQUITECTURA (Paciente 24)", "MEJOR ARQUITECTURA (Paciente 11)",
"MEJOR ARQUITECTURA (Paciente 83)", "MEJOR ARQUITECTURA (Paciente 92)"]
# Lista de DataFrames para las predicciones futuras de los pacientes
future_predictions = [future_predictions_hypo_hype_2_LSTM_22, future_predictions_hypo_hype_2_LSTM_24, future_predictions_hypo_hype_2_LSTM_11,
future_predictions_hypo_hype_2_LSTM_83, future_predictions_hypo_hype_2_LSTM_92]
# Lista de los resultados
results = [styled_result1, styled_result2, styled_result3, styled_result4, styled_result5]
# Lista de figuras y tablas zone_dfs para cada paciente
figures = [fig_hypo_hype_2_LSTM_22, fig_hypo_hype_2_LSTM_24, fig_hypo_hype_2_LSTM_11, fig_hypo_hype_2_LSTM_83, fig_hypo_hype_2_LSTM_92]
zone_dfs = [zone_df_hypo_hype_2_LSTM_22, zone_df_hypo_hype_2_LSTM_24, zone_df_hypo_hype_2_LSTM_11, zone_df_hypo_hype_2_LSTM_83, zone_df_hypo_hype_2_LSTM_92]
# Lista para guardar las tablas HTML
tables = []
# Bucle para crear y formatear las tablas
for i in range(5):
# Aplicar el estilo personalizado al DataFrame para centrar las columnas específicas
styled_df = future_predictions[i].style.set_table_styles([
{'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
])
html_table = results[i].hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + \
f"""
<div style='text-align: center;'>
<h3>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h3>
""" + styled_df.to_html().replace("<table", "<table style='margin: 0 auto;'") + "</div><br>"
# Generar un div para cada tabla con la clase 'table-container' y añadir los títulos
html_table = f"<div class='table-container'><h2>{titles[i]}</h2>{html_table}</div>"
# Añadir la tabla a la lista de tablas
tables.append(html_table)
# Bucle para mostrar las tablas, títulos y gráficas de cada paciente
for i in range(5):
# Mostrar la tabla
display(HTML(tables[i]))
# Mostrar la gráfica
display(figures[i])
# Aplicar el estilo personalizado al DataFrame para centrar las columnas
styled_df = zone_dfs[i].style.set_table_styles([
{'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
])
html_table_zone_dfs = styled_df.hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + "<br>"
# Mostrar la tabla zone_dfs
display(HTML(html_table_zone_dfs))
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.083968 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.271668 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.231488 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 4.527636 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 1.200419 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.222260 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-03-16 00:01:00 | nan | 156.141495 |
| 2022-03-16 00:16:00 | nan | 157.410568 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 11758 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.848579 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.794391 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.856952 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 5.513992 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 0.927484 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 0.975038 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-03-12 08:45:00 | nan | 156.134811 |
| 2022-03-12 09:00:00 | nan | 155.769943 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12836 | 100.00% |
| B | 0 | 0.00% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.460966 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.849688 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.868796 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 4.643505 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 1.428916 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.406802 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-02-25 19:30:00 | nan | 179.642593 |
| 2022-02-25 19:45:00 | nan | 173.928604 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12989 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 0.862907 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.610178 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.465580 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 3.425832 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 1.738084 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.239297 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-03-14 11:00:00 | nan | 166.793915 |
| 2022-03-14 11:15:00 | nan | 165.903671 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 14216 | 100.00% |
| B | 0 | 0.00% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.679124 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.603792 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.646110 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 5.643665 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 0.774303 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.264750 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-01-10 16:15:00 | nan | 186.222000 |
| 2022-01-10 16:30:00 | nan | 185.486877 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 12607 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
# Dataframes originales
df1 = styled_result1.data.copy()
df2 = styled_result2.data.copy()
df3 = styled_result3.data.copy()
df4 = styled_result4.data.copy()
df5 = styled_result5.data.copy()
# Obtener los valores mínimos de cada tabla
min_val1 = df1['Valor mínimo'].min()
min_val2 = df2['Valor mínimo'].min()
min_val3 = df3['Valor mínimo'].min()
min_val4 = df4['Valor mínimo'].min()
min_val5 = df5['Valor mínimo'].min()
# Identificar qué tabla tiene el valor mínimo más bajo
min_table_val = min(min_val1, min_val2, min_val3, min_val4, min_val5)
if min_table_val == min_val1:
min_table_name = "MEJOR ARQUITECTURA (Paciente 22)"
min_table = df1
future_prediction = future_predictions_hypo_hype_2_LSTM_22
result = styled_result1
figure = fig_hypo_hype_2_LSTM_22
zone_df = zone_df_hypo_hype_2_LSTM_22
elif min_table_val == min_val2:
min_table_name = "MEJOR ARQUITECTURA (Paciente 24)"
min_table = df2
future_prediction = future_predictions_hypo_hype_2_LSTM_24
result = styled_result2
figure = fig_hypo_hype_2_LSTM_24
zone_df = zone_df_hypo_hype_2_LSTM_24
elif min_table_val == min_val3:
min_table_name = "MEJOR ARQUITECTURA (Paciente 11)"
min_table = df3
future_prediction = future_predictions_hypo_hype_2_LSTM_11
result = styled_result3
figure = fig_hypo_hype_2_LSTM_11
zone_df = zone_df_hypo_hype_2_LSTM_11
elif min_table_val == min_val4:
min_table_name = "MEJOR ARQUITECTURA (Paciente 83)"
min_table = df4
future_prediction = future_predictions_hypo_hype_2_LSTM_83
result = styled_result4
figure = fig_hypo_hype_2_LSTM_83
zone_df = zone_df_hypo_hype_2_LSTM_83
else:
min_table_name = "MEJOR ARQUITECTURA (Paciente 92)"
min_table = df5
future_prediction = future_predictions_hypo_hype_2_LSTM_92
result = styled_result5
figure = fig_hypo_hype_2_LSTM_92
zone_df = zone_df_hypo_hype_2_LSTM_92
print(f"La tabla con el valor mínimo más bajo es: {min_table_name}")
# Definir el título para la tabla con el valor mínimo más bajo como Mejor Arquitectura (Paciente X)
min_title = f"{min_table_name}"
# Aplicar el estilo personalizado al DataFrame para centrar las columnas específicas
styled_df = future_prediction.style.set_table_styles([
{'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
])
html_table = result.hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + \
f"""
<div style='text-align: center;'>
<h3>Predicción del nivel de glucosa en los próximos 15 y 30 minutos</h3>
""" + styled_df.to_html().replace("<table", "<table style='margin: 0 auto;'") + "</div><br>"
# Generar un div para cada tabla con la clase 'table-container' y añadir los títulos y el título adicional centrado y subrayado.
html_table = f"<div class='table-container'><h1 style='text-align: center; text-decoration: underline;'>ARQUITECTURA CON MAYOR RENDIMIENTO</h1><h2>{min_title}</h2>{html_table}</div>"
# Mostrar la tabla y la gráfica del paciente con el valor mínimo más bajo
# Mostrar la tabla de resultados y predicciones futuras del paciente con el valor mínimo más bajo.
display(HTML(html_table))
# Mostrar la gráfica del paciente con el valor mínimo más bajo.
display(figure)
# Aplicar el estilo personalizado al DataFrame para centrar las columnas.
styled_df_zone_dfs_min_patient=zone_df.style.set_table_styles([
{'selector': '.col0, .col1', 'props': [('text-align', 'center')]}
])
html_table_zone_dfs_min_patient=styled_df_zone_dfs_min_patient.hide(axis='index').to_html().replace("<table", "<table style='margin: 0 auto;'") + "<br>"
# Mostrar la tabla zone_dfs del paciente con el valor mínimo más bajo.
display(HTML(html_table_zone_dfs_min_patient))
La tabla con el valor mínimo más bajo es: MEJOR ARQUITECTURA (Paciente 22)
| Evaluación+Métrica | Valor mínimo | Columna correspondiente | Arquitectura | Arquitectura (Hiperparámetros) |
|---|---|---|---|---|
| Entrenamiento MAE | 1.083968 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación MAE | 0.271668 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test MAE | 0.231488 | Measurement + Hypo_Hyper | LSTM | 2 |
| Entrenamiento RMSE | 4.527636 | Measurement + Hypo_Hyper | LSTM | 2 |
| Validación RMSE | 1.200419 | Measurement + Hypo_Hyper | LSTM | 2 |
| Test RMSE | 1.222260 | Measurement + Hypo_Hyper | LSTM | 2 |
| Datos reales | Datos predichos | |
|---|---|---|
| date | ||
| 2022-03-16 00:01:00 | nan | 156.141495 |
| 2022-03-16 00:16:00 | nan | 157.410568 |
| Zona | Conteo | Proporción |
|---|---|---|
| A | 11758 | 99.99% |
| B | 1 | 0.01% |
| C | 0 | 0.00% |
| D | 0 | 0.00% |
| E | 0 | 0.00% |
El análisis llevado a cabo en este estudio refleja una superioridad de la arquitectura LSTM con hiperparámetros 2 y las características de entrada 'Measurement + Hypo_Hyper' en la predicción de los niveles de glucosa para los próximos 15 y 30 minutos. Esta evaluación ha surgido de la comparación exhaustiva entre diferentes arquitecturas y algoritmos, incluyendo LSTM y CRNN, y una variedad de características de entrada. Esta superioridad se mantiene constante independientemente de las particularidades de los datos de entrada. La evaluación se ha aplicado a cinco pacientes seleccionados (pacientes 24, 11, 22, 83 y 92) de un conjunto de 110, generando pronósticos altamente precisos de los niveles de glucosa a corto plazo. En cada caso, los valores de predicción a 15 y 30 minutos han demostrado un mínimo Error de raíz cuadrada media (RMSE), lo que refleja la capacidad del modelo LSTM para proporcionar predicciones precisas para datos no vistos previamente.
Evaluación de los resultados finales de los 5 pacientes:
El hecho de que estos resultados sean consistentes y altamente precisos en todos los pacientes analizados proporciona una sólida verificación de la eficacia del enfoque propuesto. Por ende, podemos afirmar con confianza que el LSTM con la configuración de hiperparámetros 2 y las características de entrada 'Measurement + Hypo_Hyper' es un modelo efectivo para la predicción de los niveles de glucosa.
Es importante resaltar que el modelo LSTM exhibió una alta precisión tanto en datos de entrenamiento como en datos de prueba. Esta característica indica que el modelo está bien ajustado, sin signos de sobreajuste (overfitting) o subajuste (underfitting), lo que sugiere que tiene la capacidad de generalizar bien a datos no vistos. Esta es una cualidad esencial que refuerza la fiabilidad del modelo LSTM para la predicción de los niveles de glucosa en pacientes con diabetes.
En conclusión, los resultados obtenidos en esta investigación subrayan la eficacia de las Redes Neuronales Recurrentes de Memoria a Largo Plazo (LSTM) para predecir los niveles de glucosa en pacientes con diabetes. El alto rendimiento del modelo LSTM, confirmado a través de la comparación con la arquitectura CRNN y la variedad de características de entrada, sugiere que tiene un gran potencial para ser utilizado como una herramienta precisa y confiable en la gestión futura de la diabetes.